From d7adec50d55de9c4a29b8203e63bdf9b4e754323 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Sun, 6 Sep 2026 23:46:53 +0800 Subject: [PATCH 01/48] feat(orchestration): persist accepted task results --- scripts/orchestration/execution_context.py | 282 +++++++++++++++++++-- scripts/orchestration/task_ownership.py | 41 ++- tests/test_wor109_accepted_result.py | 217 ++++++++++++++++ 3 files changed, 514 insertions(+), 26 deletions(-) create mode 100644 tests/test_wor109_accepted_result.py diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index 17b1065..dbbebac 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -20,6 +20,7 @@ _read_structured, _as_list, _input_path, _resolve_spec_paths) from repository_preflight import capture_repository_evidence, task_caused_paths from task_ownership import ( + canonical_relative_path, OwnershipBlocker, RepairContinuity, normalize_subagent_provenance, @@ -411,9 +412,11 @@ def _task_scope_paths(values: list[Any], root: Path, label: str) -> list[str]: result: list[str] = [] for value in values: text = str(value).strip() - if not text: - raise SystemExit(f"Task {label} contains an empty path") - if _protected_project_path(text, root): + try: + text = canonical_relative_path(text, allow_tree_pattern=label == "forbidden scope") + except OwnershipBlocker as error: + raise SystemExit(f"Task {label} contains an unsafe path: {value}") from error + if label != "forbidden scope" and _protected_project_path(text, root): raise SystemExit(f"Task {label} uses a forbidden protected path: {text}") if label == "write scope" and _directory_or_module_write_path(text, root): raise SystemExit(f"Task write scope is a directory or module path and fails closed: {text}") @@ -1303,6 +1306,14 @@ def create_or_load_task_execution_binding( ) -> dict[str, Any]: control_root = control_root.expanduser().resolve() runtime_root = runtime_root.expanduser().resolve() + try: + canonical_write_scope = [canonical_relative_path(path) for path in (write_scope or [])] + canonical_forbidden_scope = [ + canonical_relative_path(path, allow_tree_pattern=True) + for path in (forbidden_scope or []) + ] + except OwnershipBlocker as error: + raise SystemExit(f"Task execution binding scope is unsafe: {error.reason}") from error from review_runtime import require_plan_reviews require_plan_reviews(control_root, _find_plan(control_root, plan_id)[0]) path = _binding_path(control_root, plan_id, task_id) @@ -1340,8 +1351,8 @@ def create_or_load_task_execution_binding( "execution_path": str(Path(str(state["path"])).resolve()), "state_path": loaded["state_path"], "git_identity": identity, - "write_scope": list(write_scope or []), - "forbidden_scope": list(forbidden_scope or []), + "write_scope": canonical_write_scope, + "forbidden_scope": canonical_forbidden_scope, "ownership": ownership, "mutating": True, "baseline": None, @@ -1384,6 +1395,208 @@ def load_task_execution_binding(control_root: Path, plan_id: str, task_id: str) return binding +ACCEPTED_TASK_RESULT_SCHEMA = "accepted-task-result-v1" +ACCEPTED_TASK_RESULT_FIELDS = { + "schema", "plan_id", "task_id", "accepted_at", "source_identity", "scope", + "binding", "repository", "ownership", "validation_evidence_ids", "review", + "acceptance_contract_identity", "executor_result_digest", "claim_identity", +} + + +def _canonical_task_scopes(task: Mapping[str, Any]) -> dict[str, list[str]]: + files = task.get("files") if isinstance(task.get("files"), Mapping) else {} + try: + return { + "read": sorted(canonical_relative_path(str(path)) for path in _as_list(files.get("read"))), + "write": sorted(canonical_relative_path(str(path)) for path in _as_list(files.get("write"))), + "forbidden": sorted( + canonical_relative_path(str(path), allow_tree_pattern=True) + for path in _as_list(files.get("forbidden")) + ), + } + except OwnershipBlocker as error: + raise SystemExit(f"accepted task result scope is unsafe: {error.reason}") from error + + +def _accepted_source_identity(task: Mapping[str, Any]) -> str: + authority = task.get("semantic_authority") if isinstance(task.get("semantic_authority"), Mapping) else {} + return semantic_digest({ + "source_ids": sorted(str(value) for value in _as_list(task.get("source_ids"))), + "records": authority.get("records", {}), + "interface_semantics": authority.get("interface_semantics", {}), + "validation_semantics": authority.get("validation_semantics", {}), + }) + + +def _accepted_contract_identity(task: Mapping[str, Any]) -> str: + validations = [ + { + key: item.get(key) + for key in ( + "id", "kind", "command", "expected", "acceptable_results", + "invariant_ids", "mechanism", "digest", + ) + if key in item + } + for item in _as_list(task.get("validation")) + if isinstance(item, Mapping) + ] + return semantic_digest({ + "review_required": task.get("review_required") is True, + "validation": validations, + "evidence_capability": task.get("evidence_capability", {}), + }) + + +def _accepted_binding_identity(binding: Mapping[str, Any]) -> dict[str, Any]: + ownership = binding.get("ownership") if isinstance(binding.get("ownership"), Mapping) else {} + baseline = binding.get("baseline") if isinstance(binding.get("baseline"), Mapping) else {} + return { + "workspace_id": str(binding.get("workspace_id") or ""), + "execution_id": str(binding.get("execution_id") or ""), + "repository_id": str(binding.get("repository_id") or ""), + "execution_path": str(Path(str(binding.get("execution_path") or "")).resolve()), + "git_identity": dict(binding.get("git_identity", {})) if isinstance(binding.get("git_identity"), Mapping) else {}, + "binding_id": str(ownership.get("binding_id") or ""), + "owner": str(ownership.get("current_owner") or ""), + "baseline": {"head": baseline.get("head"), "tree": baseline.get("tree")}, + } + + +def _accepted_repository_identity( + task: Mapping[str, Any], binding: Mapping[str, Any] +) -> dict[str, Any]: + evidence = capture_repository_evidence(Path(str(binding.get("execution_path") or "")).resolve()) + return { + "head": evidence.get("head"), + "tree": evidence.get("tree"), + "write_scope_digest": _write_scope_file_digest( + Path(str(binding.get("execution_path") or "")).resolve(), dict(task) + ), + } + + +def build_accepted_task_result( + task: Mapping[str, Any], + binding: Mapping[str, Any], + handoff: Mapping[str, Any], + validated: Mapping[str, Any], + *, + accepted_at: str | None = None, +) -> dict[str, Any]: + """Project a strongly validated executor result into compact durable authority.""" + + if validated.get("result_state") != "completed": + raise SystemExit("accepted task result requires a completed validated result") + ownership = validated.get("task_ownership") + if not isinstance(ownership, Mapping): + raise SystemExit("accepted task result requires validated subagent ownership") + if str(task.get("plan_id") or "") != str(binding.get("plan_id") or ""): + raise SystemExit("accepted task result plan binding mismatch") + if str(task.get("task_id") or "") != str(binding.get("task_id") or ""): + raise SystemExit("accepted task result task binding mismatch") + try: + accepted_ownership = normalize_subagent_provenance(ownership) + except OwnershipBlocker as error: + raise SystemExit(f"accepted task result ownership is invalid: {error.reason}") from error + review = handoff.get("acceptance_review") if isinstance(handoff.get("acceptance_review"), Mapping) else {} + if task.get("review_required") is True and ( + review.get("required") is not True or review.get("verdict") not in {"accept", "accepted"} + ): + raise SystemExit("accepted task result requires the accepted mandatory review") + observed = [item for item in _as_list(validated.get("observed_validation")) if isinstance(item, Mapping)] + validation_ids = sorted( + str( + item.get("observation_id") + or item.get("id") + or f"validation:{semantic_digest(dict(item))}" + ) + for item in observed + ) + if _as_list(task.get("validation")) and not validation_ids: + raise SystemExit("accepted task result requires observed validation evidence") + result = handoff.get("result") if isinstance(handoff.get("result"), Mapping) else {} + changes = handoff.get("changes") if isinstance(handoff.get("changes"), Mapping) else {} + fit = handoff.get("task_fit_check") if isinstance(handoff.get("task_fit_check"), Mapping) else {} + accepted = { + "schema": ACCEPTED_TASK_RESULT_SCHEMA, + "plan_id": str(task.get("plan_id") or ""), + "task_id": str(task.get("task_id") or ""), + "accepted_at": accepted_at or datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), + "source_identity": _accepted_source_identity(task), + "acceptance_contract_identity": _accepted_contract_identity(task), + "scope": _canonical_task_scopes(task), + "binding": _accepted_binding_identity(binding), + "repository": _accepted_repository_identity(task, binding), + "ownership": accepted_ownership, + "validation_evidence_ids": validation_ids, + "review": { + key: review[key] + for key in ("required", "verdict", "review_id", "review_mode", "target_identity") + if key in review + }, + "executor_result_digest": semantic_digest({ + "state": result.get("state"), + "summary": result.get("summary"), + "changes": changes.get("files", []), + "task_fit": {"task": fit.get("task"), "result": fit.get("result")}, + }), + } + accepted["claim_identity"] = semantic_digest({key: value for key, value in accepted.items() if key != "accepted_at"}) + return accepted + + +def assert_accepted_task_result_current( + task: Mapping[str, Any], binding: Mapping[str, Any], accepted: Mapping[str, Any] +) -> None: + """Fail closed when current claim-relevant authority differs from acceptance.""" + + if accepted.get("schema") != ACCEPTED_TASK_RESULT_SCHEMA: + raise SystemExit("accepted task result schema is invalid") + if set(accepted) != ACCEPTED_TASK_RESULT_FIELDS: + raise SystemExit("accepted task result shape is not closed") + expected_claim = semantic_digest({ + key: value + for key, value in accepted.items() + if key not in {"accepted_at", "claim_identity"} + }) + if accepted.get("claim_identity") != expected_claim: + raise SystemExit("accepted task result claim identity is stale or tampered") + checks = { + "plan": str(task.get("plan_id") or "") == accepted.get("plan_id"), + "task": str(task.get("task_id") or "") == accepted.get("task_id"), + "source": _accepted_source_identity(task) == accepted.get("source_identity"), + "validation/review": ( + _accepted_contract_identity(task) == accepted.get("acceptance_contract_identity") + ), + "scope": _canonical_task_scopes(task) == accepted.get("scope"), + "binding": _accepted_binding_identity(binding) == accepted.get("binding"), + "repository": _accepted_repository_identity(task, binding) == accepted.get("repository"), + } + for label, current in checks.items(): + if not current: + raise SystemExit(f"accepted task result is stale: {label} authority changed") + + +def materialize_accepted_task_result( + control_root: Path, + task: Mapping[str, Any], + handoff: Mapping[str, Any], + validated: Mapping[str, Any], + *, + accepted_at: str | None = None, +) -> dict[str, Any]: + """Persist exactly one current accepted result in the existing task binding.""" + + root = control_root.expanduser().resolve() + binding = load_task_execution_binding(root, str(task.get("plan_id") or ""), str(task.get("task_id") or "")) + accepted = build_accepted_task_result(task, binding, handoff, validated, accepted_at=accepted_at) + updated = dict(binding) + updated["accepted_result"] = accepted + _persist_binding(updated, root) + return accepted + + def capture_task_baseline_once(binding: dict[str, Any], control_root: Path | None = None) -> dict[str, Any]: existing = binding.get("baseline") if isinstance(existing, dict) and existing.get("head"): @@ -1407,10 +1620,16 @@ def capture_task_baseline_once(binding: dict[str, Any], control_root: Path | Non def _write_scope_file_digest(execution_root: Path, task: dict[str, Any]) -> str: digest = hashlib.sha256() files = task.get("files") if isinstance(task.get("files"), dict) else {} - for relative in _as_list(files.get("write")): - digest.update(str(relative).encode("utf-8")) + try: + paths = sorted( + canonical_relative_path(str(relative)) for relative in _as_list(files.get("write")) + ) + except OwnershipBlocker as error: + raise SystemExit(f"Declared write scope is unsafe: {error.reason}") from error + for relative in paths: + digest.update(relative.encode("utf-8")) digest.update(b"\0") - path = execution_root / str(relative) + path = execution_root / relative if path.is_file() and not path.is_symlink(): digest.update(path.read_bytes()) else: @@ -1474,9 +1693,15 @@ def _observe_validation_item(item: dict[str, Any], execution_root: Path, task: d def _path_is_forbidden(relative: str, forbidden: list[str]) -> bool: - normalized = relative.removeprefix("./") + try: + normalized = canonical_relative_path(relative) + except OwnershipBlocker as error: + raise SystemExit(f"Observed mutation path is unsafe: {relative}") from error for pattern in forbidden: - pat = str(pattern).removeprefix("./") + try: + pat = canonical_relative_path(str(pattern), allow_tree_pattern=True) + except OwnershipBlocker as error: + raise SystemExit(f"Declared forbidden scope is unsafe: {pattern}") from error if pat.endswith("/**"): prefix = pat[:-3] if normalized == prefix or normalized.startswith(f"{prefix}/"): @@ -3375,13 +3600,22 @@ def _assert_task_fit_check(handoff: dict[str, Any], task_id: str, state: str) -> def _assert_changed_paths_in_write_scope(handoff: dict[str, Any], task_files: dict[str, Any]) -> None: - write_scope = {str(path).strip().removeprefix("./") for path in _as_list(task_files.get("write"))} + try: + write_scope = { + canonical_relative_path(str(path)) for path in _as_list(task_files.get("write")) + } + except OwnershipBlocker as error: + raise SystemExit(f"Declared write scope is unsafe: {error.reason}") from error changes = handoff.get("changes") if isinstance(handoff.get("changes"), dict) else {} for item in _as_list(changes.get("files")): if not isinstance(item, dict): continue - path = str(item.get("path") or "").strip().removeprefix("./") - if path and path not in write_scope: + path = str(item.get("path") or "").strip() + try: + canonical = canonical_relative_path(path) if path else "" + except OwnershipBlocker as error: + raise SystemExit(f"Executor result changed path is unsafe: {path}") from error + if canonical and canonical not in write_scope: raise SystemExit(f"Executor result changed path is outside task write scope: {path}") @@ -3609,7 +3843,11 @@ def _compile_task_brief(args: argparse.Namespace) -> tuple[Path, dict[str, Any]] write_files = _task_scope_paths( _as_list(files.get("write")) or _as_list(task.get("target_files")), root, "write scope" ) - forbidden_files = _as_list(files.get("forbidden")) or _as_list(task.get("forbidden_files")) + forbidden_files = _task_scope_paths( + _as_list(files.get("forbidden")) or _as_list(task.get("forbidden_files")), + root, + "forbidden scope", + ) methodology = task.get("methodology") if isinstance(task.get("methodology"), dict) else {} allocated_skills = [item for item in _as_list(task.get("allocated_skills")) if isinstance(item, dict)] @@ -3770,9 +4008,15 @@ def _paths_from_name_status(line: str) -> list[str]: def _write_scope_match(path: str, write_paths: list[str]) -> bool: - normalized = path.removeprefix("./") + try: + normalized = canonical_relative_path(path) + except OwnershipBlocker as error: + raise SystemExit(f"Observed mutation path is unsafe: {path}") from error for write in write_paths: - write_n = str(write).removeprefix("./").rstrip("/") + try: + write_n = canonical_relative_path(str(write)) + except OwnershipBlocker as error: + raise SystemExit(f"Declared write scope is unsafe: {write}") from error if normalized == write_n or normalized.startswith(f"{write_n}/"): return True return False @@ -4132,7 +4376,11 @@ def cmd_validate_executor_result(args: argparse.Namespace) -> None: handoff_root = root / ".work-bundle/orchestration/handoff" handoff_path = _input_path(args.handoff, root, handoff_root, "handoff") handoff, _ = _read_structured(handoff_path) - validate_executor_result_for_task(handoff, task, observe=True, **_observation_kwargs(args)) + validated = validate_executor_result_for_task( + handoff, task, observe=True, **_observation_kwargs(args) + ) + if validated.get("result_state") == "completed": + materialize_accepted_task_result(root, task, handoff, validated) print(handoff_path.relative_to(root).as_posix()) diff --git a/scripts/orchestration/task_ownership.py b/scripts/orchestration/task_ownership.py index 72e8e9f..12604c0 100644 --- a/scripts/orchestration/task_ownership.py +++ b/scripts/orchestration/task_ownership.py @@ -22,6 +22,30 @@ def __init__(self, code: str, reason: str) -> None: super().__init__(f"{code}: {reason}") +def canonical_relative_path(path: str, *, allow_tree_pattern: bool = False) -> str: + """Return one safe POSIX-relative meaning for a path or narrow tree scope.""" + + text = str(path).strip() + tree_pattern = allow_tree_pattern and text.endswith("/**") + if tree_pattern: + text = text[:-3] + if ( + not text + or text in {".", "./"} + or text.startswith("/") + or "\\" in text + or any(character in text for character in "*?[]") + ): + raise OwnershipBlocker("workspace-blocked", "scope path is empty or unsafe") + parsed = PurePosixPath(text) + if parsed.is_absolute() or ".." in parsed.parts: + raise OwnershipBlocker("workspace-blocked", "scope path is unsafe") + normalized = parsed.as_posix() + if not normalized or normalized == ".": + raise OwnershipBlocker("workspace-blocked", "scope path is empty or unsafe") + return f"{normalized}/**" if tree_pattern else normalized + + @dataclass(frozen=True) class TaskCandidate: task_id: str @@ -41,6 +65,8 @@ def __post_init__(self) -> None: raise ValueError("write_scope must contain explicit paths") if not self.execution_workspace.strip(): raise ValueError("execution_workspace must be non-empty") + for path in self.write_scope: + canonical_relative_path(path) @dataclass(frozen=True) @@ -121,8 +147,8 @@ def normalize_subagent_provenance( def _scope_matches(path: str, scope: str) -> bool: - left = path.strip().removeprefix("./").rstrip("/") - right = scope.strip().removeprefix("./").rstrip("/") + left = canonical_relative_path(path) + right = canonical_relative_path(scope, allow_tree_pattern=True).removesuffix("/**") return left == right or left.startswith(right + "/") or right.startswith(left + "/") @@ -154,13 +180,10 @@ def validate_task_acceptance_ownership( for path in paths: if not isinstance(path, str): raise OwnershipBlocker("review-blocked", "mutation event paths must contain strings") - normalized = path.strip().removeprefix("./") - parsed = PurePosixPath(normalized) - if (not normalized or parsed.is_absolute() or ".." in parsed.parts - or normalized in {".", "./"} or "\\" in normalized - or any(character in normalized for character in "*?[]")): - raise OwnershipBlocker("review-blocked", "mutation event path is unsafe") - changed.append(parsed.as_posix()) + try: + changed.append(canonical_relative_path(path)) + except OwnershipBlocker as error: + raise OwnershipBlocker("review-blocked", "mutation event path is unsafe") from error if actor_kind != "controller": continue if _scopes_overlap(changed, write_scope): diff --git a/tests/test_wor109_accepted_result.py b/tests/test_wor109_accepted_result.py new file mode 100644 index 0000000..14a1659 --- /dev/null +++ b/tests/test_wor109_accepted_result.py @@ -0,0 +1,217 @@ +from __future__ import annotations + +from copy import deepcopy +from pathlib import Path +import sys + +import pytest + + +ORCHESTRATION = Path(__file__).resolve().parents[1] / "scripts" / "orchestration" +sys.path.insert(0, str(ORCHESTRATION)) + +import execution_context # noqa: E402 +from task_ownership import ( # noqa: E402 + OwnershipBlocker, + TaskCandidate, + canonical_relative_path, + validate_task_acceptance_ownership, +) + + +OID_A = "a" * 40 +OID_B = "b" * 40 + + +def _task(root: Path) -> dict[str, object]: + return { + "plan_id": "plan-001", + "task_id": "task-001", + "depends_on": ["task-000"], + "source_ids": ["REQ-001"], + "files": { + "read": ["src/./read.py"], + "write": ["src//a.py"], + "forbidden": ["secrets/key.txt"], + }, + "validation": [ + { + "id": "VAL-001", + "kind": "process", + "command": "pytest -q", + "boundary": "component", + "freshness": "current_task_batch", + } + ], + "review_required": True, + "executor_profile": {"capability": "judgment"}, + "workspace": {"root": str(root)}, + } + + +def _binding(root: Path) -> dict[str, object]: + return { + "plan_id": "plan-001", + "task_id": "task-001", + "workspace_id": "ws-001", + "execution_id": "exec-001", + "repository_id": "repo-001", + "execution_path": str(root), + "git_identity": {"branch_ref": "refs/heads/main"}, + "baseline": {"head": OID_A, "tree": OID_B}, + "ownership": { + "binding_id": "binding:plan-001:task-001", + "state": "active", + "current_owner": "task-001", + "history": [{"event": "created"}], + }, + } + + +def _handoff() -> dict[str, object]: + return { + "type": "executor-result", + "related": {"plan": "plan-001", "task": "task-001"}, + "result": {"state": "completed", "summary": "Implemented the bounded slice."}, + "changes": {"files": [{"path": "src/a.py", "change": "updated"}]}, + "task_fit_check": {"task": "task-001", "result": "clean"}, + "knowledge_disposition": {"action": "none", "affected_authority": []}, + "acceptance_review": {"required": True, "verdict": "accept", "review_id": "review-001"}, + "delegation_evidence": { + "delegated": True, + "owner_kind": "subagent", + "agent_id": "agent-001", + "run_id": "run-001", + "mechanism": "host-native", + }, + "validation": {"commands": [{"command": "pytest -q", "result": "passed"}]}, + } + + +def _validated() -> dict[str, object]: + return { + "result_state": "completed", + "task_ownership": { + "delegated": True, + "owner_kind": "subagent", + "agent_id": "agent-001", + "run_id": "run-001", + "mechanism": "host-native", + }, + "observed_validation": [{"id": "VAL-001", "observation_id": "obs-001", "result": "passed"}], + } + + +def test_shared_scope_canonicalizer_normalizes_equivalent_paths_and_rejects_unsafe() -> None: + assert canonical_relative_path("src/./a.py") == "src/a.py" + assert canonical_relative_path("src//a.py") == "src/a.py" + + for unsafe in ("", ".", "../src/a.py", "/src/a.py", "src\\a.py", "src/*.py"): + with pytest.raises(OwnershipBlocker, match="unsafe|empty"): + canonical_relative_path(unsafe) + + with pytest.raises(SystemExit, match="unsafe"): + execution_context._task_scope_paths(["../src/a.py"], Path.cwd(), "write scope") + + +def test_declared_and_observed_scopes_use_the_same_canonical_semantics() -> None: + with pytest.raises(OwnershipBlocker, match="controller mutated"): + validate_task_acceptance_ownership( + delegation_evidence=_handoff()["delegation_evidence"], + mutation_events=[{"actor_kind": "controller", "paths": ["src/./a.py"]}], + write_scope=["src//a.py"], + validations_passed=True, + ) + + with pytest.raises(OwnershipBlocker, match="unsafe"): + TaskCandidate( + task_id="task-001", + dependencies=(), + write_scope=("../outside.py",), + execution_workspace="bound-worktree", + ) + + +def test_accepted_result_is_deterministic_current_authority_not_handoff_history( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + task = _task(tmp_path) + binding = _binding(tmp_path) + monkeypatch.setattr( + execution_context, + "capture_repository_evidence", + lambda _root: {"head": OID_A, "tree": OID_B, "entries": {}, "status": "dirty"}, + ) + + first = execution_context.build_accepted_task_result( + task, binding, _handoff(), _validated(), accepted_at="2026-09-06T10:00:00Z" + ) + appended = deepcopy(binding) + appended["ownership"]["history"].append({"event": "audit-appended"}) + second = execution_context.build_accepted_task_result( + task, appended, _handoff(), _validated(), accepted_at="2026-09-06T10:00:00Z" + ) + + assert first == second + assert first["schema"] == "accepted-task-result-v1" + assert first["validation_evidence_ids"] == ["obs-001"] + assert "mutation_events" not in repr(first) + assert "validation" not in first["executor_result_digest"] + execution_context.assert_accepted_task_result_current(task, appended, first) + + changed_scope = deepcopy(task) + changed_scope["files"]["write"] = ["src/other.py"] + with pytest.raises(SystemExit, match="accepted task result.*scope"): + execution_context.assert_accepted_task_result_current(changed_scope, appended, first) + + changed_source = deepcopy(task) + changed_source["source_ids"] = ["REQ-002"] + with pytest.raises(SystemExit, match="accepted task result.*source"): + execution_context.assert_accepted_task_result_current(changed_source, appended, first) + + changed_binding = deepcopy(binding) + changed_binding["execution_id"] = "exec-002" + with pytest.raises(SystemExit, match="accepted task result.*binding"): + execution_context.assert_accepted_task_result_current(task, changed_binding, first) + + tampered = deepcopy(first) + tampered["ownership"]["agent_id"] = "other-agent" + with pytest.raises(SystemExit, match="tampered"): + execution_context.assert_accepted_task_result_current(task, binding, tampered) + + changed_review = deepcopy(task) + changed_review["review_required"] = False + with pytest.raises(SystemExit, match="validation/review"): + execution_context.assert_accepted_task_result_current(changed_review, binding, first) + + (tmp_path / "src").mkdir() + (tmp_path / "src/a.py").write_text("changed after acceptance\n", encoding="utf-8") + with pytest.raises(SystemExit, match="repository"): + execution_context.assert_accepted_task_result_current(task, binding, first) + + +def test_materialize_persists_one_result_in_existing_binding( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + task = _task(tmp_path) + binding = _binding(tmp_path) + persisted: list[dict[str, object]] = [] + monkeypatch.setattr(execution_context, "load_task_execution_binding", lambda *_args: deepcopy(binding)) + monkeypatch.setattr(execution_context, "_persist_binding", lambda value, _root: persisted.append(value)) + monkeypatch.setattr( + execution_context, + "capture_repository_evidence", + lambda _root: {"head": OID_A, "tree": OID_B, "entries": {}, "status": "dirty"}, + ) + + accepted = execution_context.materialize_accepted_task_result( + tmp_path, + task, + _handoff(), + _validated(), + accepted_at="2026-09-06T10:00:00Z", + ) + + assert len(persisted) == 1 + assert persisted[0]["accepted_result"] == accepted + assert persisted[0]["ownership"] == binding["ownership"] From 5b6da5af2c4352e9621808b787b7d6e6c0d977b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 00:00:55 +0800 Subject: [PATCH 02/48] fix(orchestration): close accepted result projections --- scripts/orchestration/execution_context.py | 229 +++++++++++++++------ tests/test_wor109_accepted_result.py | 80 ++++++- 2 files changed, 235 insertions(+), 74 deletions(-) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index dbbebac..af66089 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -1397,9 +1397,14 @@ def load_task_execution_binding(control_root: Path, plan_id: str, task_id: str) ACCEPTED_TASK_RESULT_SCHEMA = "accepted-task-result-v1" ACCEPTED_TASK_RESULT_FIELDS = { - "schema", "plan_id", "task_id", "accepted_at", "source_identity", "scope", - "binding", "repository", "ownership", "validation_evidence_ids", "review", - "acceptance_contract_identity", "executor_result_digest", "claim_identity", + "schema", "plan_id", "task_id", "binding_id", "baseline_identity", + "accepted_source", "authority_projection", "executor_result_digest", + "validation_evidence_ids", "review_id", "owner_identity", "accepted_at", + "invalidation", +} +ACCEPTED_AUTHORITY_PROJECTION_FIELDS = { + "task_digest", "binding_digest", "scope_digest", "validation_obligations_digest", + "required_review_digest", "ownership_digest", } @@ -1418,39 +1423,40 @@ def _canonical_task_scopes(task: Mapping[str, Any]) -> dict[str, list[str]]: raise SystemExit(f"accepted task result scope is unsafe: {error.reason}") from error -def _accepted_source_identity(task: Mapping[str, Any]) -> str: +def _accepted_task_projection(task: Mapping[str, Any]) -> dict[str, Any]: authority = task.get("semantic_authority") if isinstance(task.get("semantic_authority"), Mapping) else {} - return semantic_digest({ + topology_fields = ( + "phase_id", "parallel_group", "common_contract", "barrier", + "barrier_participants", "convergence_owner", "integration_owner", + ) + return { + "plan_id": str(task.get("plan_id") or ""), + "task_id": str(task.get("task_id") or ""), + "depends_on": sorted(str(value) for value in _as_list(task.get("depends_on"))), + "topology": {key: task.get(key) for key in topology_fields if key in task}, "source_ids": sorted(str(value) for value in _as_list(task.get("source_ids"))), - "records": authority.get("records", {}), - "interface_semantics": authority.get("interface_semantics", {}), - "validation_semantics": authority.get("validation_semantics", {}), - }) + "semantic_authority": { + "records": authority.get("records", {}), + "interface_semantics": authority.get("interface_semantics", {}), + "validation_semantics": authority.get("validation_semantics", {}), + }, + } -def _accepted_contract_identity(task: Mapping[str, Any]) -> str: - validations = [ +def _accepted_validation_projection(task: Mapping[str, Any]) -> list[dict[str, Any]]: + return [ { key: item.get(key) - for key in ( - "id", "kind", "command", "expected", "acceptable_results", - "invariant_ids", "mechanism", "digest", - ) + for key in ("id", "command", "boundary", "freshness") if key in item } for item in _as_list(task.get("validation")) if isinstance(item, Mapping) ] - return semantic_digest({ - "review_required": task.get("review_required") is True, - "validation": validations, - "evidence_capability": task.get("evidence_capability", {}), - }) -def _accepted_binding_identity(binding: Mapping[str, Any]) -> dict[str, Any]: +def _accepted_binding_projection(binding: Mapping[str, Any]) -> dict[str, Any]: ownership = binding.get("ownership") if isinstance(binding.get("ownership"), Mapping) else {} - baseline = binding.get("baseline") if isinstance(binding.get("baseline"), Mapping) else {} return { "workspace_id": str(binding.get("workspace_id") or ""), "execution_id": str(binding.get("execution_id") or ""), @@ -1458,24 +1464,75 @@ def _accepted_binding_identity(binding: Mapping[str, Any]) -> dict[str, Any]: "execution_path": str(Path(str(binding.get("execution_path") or "")).resolve()), "git_identity": dict(binding.get("git_identity", {})) if isinstance(binding.get("git_identity"), Mapping) else {}, "binding_id": str(ownership.get("binding_id") or ""), - "owner": str(ownership.get("current_owner") or ""), - "baseline": {"head": baseline.get("head"), "tree": baseline.get("tree")}, + "baseline_identity": semantic_digest( + dict(binding.get("baseline", {})) if isinstance(binding.get("baseline"), Mapping) else {} + ), } -def _accepted_repository_identity( - task: Mapping[str, Any], binding: Mapping[str, Any] +def _accepted_review_projection(task: Mapping[str, Any], review_id: str) -> dict[str, Any]: + return { + "required": task.get("review_required") is True, + "review_mode": task.get("review_mode", "initial"), + "repair_frontier": task.get("repair_frontier"), + "accepted_verdict_identity": { + "review_id": review_id, + "verdict": "accepted", + }, + } + + +def _accepted_ownership_projection( + task: Mapping[str, Any], binding: Mapping[str, Any], owner_identity: Mapping[str, Any] ) -> dict[str, Any]: - evidence = capture_repository_evidence(Path(str(binding.get("execution_path") or "")).resolve()) + ownership = binding.get("ownership") if isinstance(binding.get("ownership"), Mapping) else {} return { - "head": evidence.get("head"), - "tree": evidence.get("tree"), - "write_scope_digest": _write_scope_file_digest( - Path(str(binding.get("execution_path") or "")).resolve(), dict(task) + "required_executor_profile": task.get("executor_profile", {}), + "binding_id": str(ownership.get("binding_id") or ""), + "original_owner": str(ownership.get("original_owner") or task.get("task_id") or ""), + "owner_identity": dict(owner_identity), + } + + +def _accepted_authority_projection( + task: Mapping[str, Any], + binding: Mapping[str, Any], + *, + review_id: str, + owner_identity: Mapping[str, Any], +) -> dict[str, str]: + return { + "task_digest": semantic_digest(_accepted_task_projection(task)), + "binding_digest": semantic_digest(_accepted_binding_projection(binding)), + "scope_digest": semantic_digest(_canonical_task_scopes(task)), + "validation_obligations_digest": semantic_digest(_accepted_validation_projection(task)), + "required_review_digest": semantic_digest(_accepted_review_projection(task, review_id)), + "ownership_digest": semantic_digest( + _accepted_ownership_projection(task, binding, owner_identity) ), } +def _accepted_source_state_digest( + *, + plan_id: str, + task_id: str, + binding_id: str, + baseline_identity: str, + head: object, + tree: object, + authority_projection: Mapping[str, Any], +) -> str: + return semantic_digest({ + "plan_id": plan_id, + "task_id": task_id, + "binding_id": binding_id, + "baseline_identity": baseline_identity, + "accepted_source": {"head": head, "tree": tree}, + "authority_projection": dict(authority_projection), + }) + + def build_accepted_task_result( task: Mapping[str, Any], binding: Mapping[str, Any], @@ -1515,35 +1572,55 @@ def build_accepted_task_result( ) if _as_list(task.get("validation")) and not validation_ids: raise SystemExit("accepted task result requires observed validation evidence") + review_id = str(review.get("review_id") or "") + if task.get("review_required") is True and not review_id: + raise SystemExit("accepted task result requires an accepted review identity") result = handoff.get("result") if isinstance(handoff.get("result"), Mapping) else {} changes = handoff.get("changes") if isinstance(handoff.get("changes"), Mapping) else {} fit = handoff.get("task_fit_check") if isinstance(handoff.get("task_fit_check"), Mapping) else {} - accepted = { + plan_id = str(task.get("plan_id") or "") + task_id = str(task.get("task_id") or "") + binding_ownership = binding.get("ownership") if isinstance(binding.get("ownership"), Mapping) else {} + binding_id = str(binding_ownership.get("binding_id") or "") + baseline = binding.get("baseline") if isinstance(binding.get("baseline"), Mapping) else {} + baseline_identity = semantic_digest(dict(baseline)) + authority_projection = _accepted_authority_projection( + task, + binding, + review_id=review_id, + owner_identity=accepted_ownership, + ) + evidence = capture_repository_evidence(Path(str(binding.get("execution_path") or "")).resolve()) + accepted_source = {"head": evidence.get("head"), "tree": evidence.get("tree")} + accepted_source["state_digest"] = _accepted_source_state_digest( + plan_id=plan_id, + task_id=task_id, + binding_id=binding_id, + baseline_identity=baseline_identity, + head=accepted_source["head"], + tree=accepted_source["tree"], + authority_projection=authority_projection, + ) + return { "schema": ACCEPTED_TASK_RESULT_SCHEMA, - "plan_id": str(task.get("plan_id") or ""), - "task_id": str(task.get("task_id") or ""), - "accepted_at": accepted_at or datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), - "source_identity": _accepted_source_identity(task), - "acceptance_contract_identity": _accepted_contract_identity(task), - "scope": _canonical_task_scopes(task), - "binding": _accepted_binding_identity(binding), - "repository": _accepted_repository_identity(task, binding), - "ownership": accepted_ownership, - "validation_evidence_ids": validation_ids, - "review": { - key: review[key] - for key in ("required", "verdict", "review_id", "review_mode", "target_identity") - if key in review - }, + "plan_id": plan_id, + "task_id": task_id, + "binding_id": binding_id, + "baseline_identity": baseline_identity, + "accepted_source": accepted_source, + "authority_projection": authority_projection, "executor_result_digest": semantic_digest({ "state": result.get("state"), "summary": result.get("summary"), "changes": changes.get("files", []), "task_fit": {"task": fit.get("task"), "result": fit.get("result")}, }), + "validation_evidence_ids": validation_ids, + "review_id": review_id, + "owner_identity": accepted_ownership, + "accepted_at": accepted_at or datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), + "invalidation": None, } - accepted["claim_identity"] = semantic_digest({key: value for key, value in accepted.items() if key != "accepted_at"}) - return accepted def assert_accepted_task_result_current( @@ -1555,23 +1632,49 @@ def assert_accepted_task_result_current( raise SystemExit("accepted task result schema is invalid") if set(accepted) != ACCEPTED_TASK_RESULT_FIELDS: raise SystemExit("accepted task result shape is not closed") - expected_claim = semantic_digest({ - key: value - for key, value in accepted.items() - if key not in {"accepted_at", "claim_identity"} - }) - if accepted.get("claim_identity") != expected_claim: - raise SystemExit("accepted task result claim identity is stale or tampered") + if accepted.get("invalidation") is not None: + raise SystemExit("accepted task result was explicitly invalidated") + accepted_source = accepted.get("accepted_source") + authority_projection = accepted.get("authority_projection") + owner_identity = accepted.get("owner_identity") + if not isinstance(accepted_source, Mapping) or set(accepted_source) != {"head", "tree", "state_digest"}: + raise SystemExit("accepted task result source shape is not closed") + if not isinstance(authority_projection, Mapping) or set(authority_projection) != ACCEPTED_AUTHORITY_PROJECTION_FIELDS: + raise SystemExit("accepted task result authority projection is not closed") + if not isinstance(owner_identity, Mapping): + raise SystemExit("accepted task result owner identity is invalid") + current_projection = _accepted_authority_projection( + task, + binding, + review_id=str(accepted.get("review_id") or ""), + owner_identity=owner_identity, + ) + binding_ownership = binding.get("ownership") if isinstance(binding.get("ownership"), Mapping) else {} + baseline = binding.get("baseline") if isinstance(binding.get("baseline"), Mapping) else {} checks = { "plan": str(task.get("plan_id") or "") == accepted.get("plan_id"), - "task": str(task.get("task_id") or "") == accepted.get("task_id"), - "source": _accepted_source_identity(task) == accepted.get("source_identity"), - "validation/review": ( - _accepted_contract_identity(task) == accepted.get("acceptance_contract_identity") + "task": ( + str(task.get("task_id") or "") == accepted.get("task_id") + and current_projection["task_digest"] == authority_projection.get("task_digest") + ), + "binding": ( + str(binding_ownership.get("binding_id") or "") == accepted.get("binding_id") + and semantic_digest(dict(baseline)) == accepted.get("baseline_identity") + and current_projection["binding_digest"] == authority_projection.get("binding_digest") + ), + "scope": current_projection["scope_digest"] == authority_projection.get("scope_digest"), + "validation": current_projection["validation_obligations_digest"] == authority_projection.get("validation_obligations_digest"), + "review": current_projection["required_review_digest"] == authority_projection.get("required_review_digest"), + "ownership": current_projection["ownership_digest"] == authority_projection.get("ownership_digest"), + "source": accepted_source.get("state_digest") == _accepted_source_state_digest( + plan_id=str(accepted.get("plan_id") or ""), + task_id=str(accepted.get("task_id") or ""), + binding_id=str(accepted.get("binding_id") or ""), + baseline_identity=str(accepted.get("baseline_identity") or ""), + head=accepted_source.get("head"), + tree=accepted_source.get("tree"), + authority_projection=authority_projection, ), - "scope": _canonical_task_scopes(task) == accepted.get("scope"), - "binding": _accepted_binding_identity(binding) == accepted.get("binding"), - "repository": _accepted_repository_identity(task, binding) == accepted.get("repository"), } for label, current in checks.items(): if not current: diff --git a/tests/test_wor109_accepted_result.py b/tests/test_wor109_accepted_result.py index 14a1659..9f88556 100644 --- a/tests/test_wor109_accepted_result.py +++ b/tests/test_wor109_accepted_result.py @@ -21,6 +21,24 @@ OID_A = "a" * 40 OID_B = "b" * 40 +OID_C = "c" * 40 +OID_D = "d" * 40 + +ACCEPTED_RESULT_FIELDS = { + "schema", + "plan_id", + "task_id", + "binding_id", + "baseline_identity", + "accepted_source", + "authority_projection", + "executor_result_digest", + "validation_evidence_ids", + "review_id", + "owner_identity", + "accepted_at", + "invalidation", +} def _task(root: Path) -> dict[str, object]: @@ -153,7 +171,17 @@ def test_accepted_result_is_deterministic_current_authority_not_handoff_history( ) assert first == second + assert set(first) == ACCEPTED_RESULT_FIELDS assert first["schema"] == "accepted-task-result-v1" + assert set(first["authority_projection"]) == { + "task_digest", + "binding_digest", + "scope_digest", + "validation_obligations_digest", + "required_review_digest", + "ownership_digest", + } + assert set(first["accepted_source"]) == {"head", "tree", "state_digest"} assert first["validation_evidence_ids"] == ["obs-001"] assert "mutation_events" not in repr(first) assert "validation" not in first["executor_result_digest"] @@ -166,7 +194,7 @@ def test_accepted_result_is_deterministic_current_authority_not_handoff_history( changed_source = deepcopy(task) changed_source["source_ids"] = ["REQ-002"] - with pytest.raises(SystemExit, match="accepted task result.*source"): + with pytest.raises(SystemExit, match="accepted task result.*task"): execution_context.assert_accepted_task_result_current(changed_source, appended, first) changed_binding = deepcopy(binding) @@ -174,20 +202,50 @@ def test_accepted_result_is_deterministic_current_authority_not_handoff_history( with pytest.raises(SystemExit, match="accepted task result.*binding"): execution_context.assert_accepted_task_result_current(task, changed_binding, first) - tampered = deepcopy(first) - tampered["ownership"]["agent_id"] = "other-agent" - with pytest.raises(SystemExit, match="tampered"): - execution_context.assert_accepted_task_result_current(task, binding, tampered) - changed_review = deepcopy(task) changed_review["review_required"] = False - with pytest.raises(SystemExit, match="validation/review"): + with pytest.raises(SystemExit, match="review"): execution_context.assert_accepted_task_result_current(changed_review, binding, first) - (tmp_path / "src").mkdir() - (tmp_path / "src/a.py").write_text("changed after acceptance\n", encoding="utf-8") - with pytest.raises(SystemExit, match="repository"): - execution_context.assert_accepted_task_result_current(task, binding, first) + invalidated = deepcopy(first) + invalidated["invalidation"] = {"reason": "accepted source changed"} + with pytest.raises(SystemExit, match="explicitly invalidated"): + execution_context.assert_accepted_task_result_current(task, binding, invalidated) + + +def test_unrelated_repository_advance_does_not_stale_accepted_task_result( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + repository = {"head": OID_A, "tree": OID_B, "entries": {}, "status": "dirty"} + monkeypatch.setattr(execution_context, "capture_repository_evidence", lambda _root: repository) + task = _task(tmp_path) + binding = _binding(tmp_path) + accepted = execution_context.build_accepted_task_result( + task, binding, _handoff(), _validated(), accepted_at="2026-09-06T10:00:00Z" + ) + + repository = {"head": OID_C, "tree": OID_D, "entries": {}, "status": "clean"} + execution_context.assert_accepted_task_result_current(task, binding, accepted) + + +def test_dependency_topology_change_invalidates_accepted_task_result( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setattr( + execution_context, + "capture_repository_evidence", + lambda _root: {"head": OID_A, "tree": OID_B, "entries": {}, "status": "dirty"}, + ) + task = _task(tmp_path) + binding = _binding(tmp_path) + accepted = execution_context.build_accepted_task_result( + task, binding, _handoff(), _validated(), accepted_at="2026-09-06T10:00:00Z" + ) + + changed = deepcopy(task) + changed["depends_on"] = ["task-other"] + with pytest.raises(SystemExit, match="task"): + execution_context.assert_accepted_task_result_current(changed, binding, accepted) def test_materialize_persists_one_result_in_existing_binding( From 7cad5b3f844893e53d4c2d96e8ee696cfa9c071d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 00:10:51 +0800 Subject: [PATCH 03/48] fix(orchestration): bind accepted review identity --- scripts/orchestration/execution_context.py | 72 +++++++++++++--------- tests/test_wor109_accepted_result.py | 54 ++++++++++++++++ 2 files changed, 96 insertions(+), 30 deletions(-) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index af66089..b283698 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -1464,22 +1464,28 @@ def _accepted_binding_projection(binding: Mapping[str, Any]) -> dict[str, Any]: "execution_path": str(Path(str(binding.get("execution_path") or "")).resolve()), "git_identity": dict(binding.get("git_identity", {})) if isinstance(binding.get("git_identity"), Mapping) else {}, "binding_id": str(ownership.get("binding_id") or ""), - "baseline_identity": semantic_digest( - dict(binding.get("baseline", {})) if isinstance(binding.get("baseline"), Mapping) else {} - ), + "baseline_identity": _accepted_baseline_identity(binding), } -def _accepted_review_projection(task: Mapping[str, Any], review_id: str) -> dict[str, Any]: - return { - "required": task.get("review_required") is True, - "review_mode": task.get("review_mode", "initial"), - "repair_frontier": task.get("repair_frontier"), - "accepted_verdict_identity": { - "review_id": review_id, - "verdict": "accepted", - }, +def _accepted_baseline_identity(binding: Mapping[str, Any]) -> dict[str, str]: + baseline = binding.get("baseline") if isinstance(binding.get("baseline"), Mapping) else {} + identity = {"head": str(baseline.get("head") or ""), "tree": str(baseline.get("tree") or "")} + if any(not re.fullmatch(r"[0-9a-f]{40}|[0-9a-f]{64}", value) for value in identity.values()): + raise SystemExit("accepted task result baseline identity is invalid") + return identity + + +def _accepted_review_projection(review: Mapping[str, Any]) -> dict[str, Any]: + projection: dict[str, Any] = { + "required": review.get("required") is True, + "review_id": str(review.get("review_id")) if review.get("review_id") else None, + "verdict": "accepted" if review.get("verdict") in {"accept", "accepted"} else review.get("verdict"), } + for key in ("review_mode", "repair_frontier", "target_identity"): + if key in review and review.get(key) is not None: + projection[key] = review[key] + return projection def _accepted_ownership_projection( @@ -1498,7 +1504,7 @@ def _accepted_authority_projection( task: Mapping[str, Any], binding: Mapping[str, Any], *, - review_id: str, + accepted_review: Mapping[str, Any], owner_identity: Mapping[str, Any], ) -> dict[str, str]: return { @@ -1506,7 +1512,7 @@ def _accepted_authority_projection( "binding_digest": semantic_digest(_accepted_binding_projection(binding)), "scope_digest": semantic_digest(_canonical_task_scopes(task)), "validation_obligations_digest": semantic_digest(_accepted_validation_projection(task)), - "required_review_digest": semantic_digest(_accepted_review_projection(task, review_id)), + "required_review_digest": semantic_digest(_accepted_review_projection(accepted_review)), "ownership_digest": semantic_digest( _accepted_ownership_projection(task, binding, owner_identity) ), @@ -1518,7 +1524,7 @@ def _accepted_source_state_digest( plan_id: str, task_id: str, binding_id: str, - baseline_identity: str, + baseline_identity: Mapping[str, str], head: object, tree: object, authority_projection: Mapping[str, Any], @@ -1527,7 +1533,7 @@ def _accepted_source_state_digest( "plan_id": plan_id, "task_id": task_id, "binding_id": binding_id, - "baseline_identity": baseline_identity, + "baseline_identity": dict(baseline_identity), "accepted_source": {"head": head, "tree": tree}, "authority_projection": dict(authority_projection), }) @@ -1572,7 +1578,7 @@ def build_accepted_task_result( ) if _as_list(task.get("validation")) and not validation_ids: raise SystemExit("accepted task result requires observed validation evidence") - review_id = str(review.get("review_id") or "") + review_id = str(review.get("review_id")) if review.get("review_id") else None if task.get("review_required") is True and not review_id: raise SystemExit("accepted task result requires an accepted review identity") result = handoff.get("result") if isinstance(handoff.get("result"), Mapping) else {} @@ -1582,12 +1588,11 @@ def build_accepted_task_result( task_id = str(task.get("task_id") or "") binding_ownership = binding.get("ownership") if isinstance(binding.get("ownership"), Mapping) else {} binding_id = str(binding_ownership.get("binding_id") or "") - baseline = binding.get("baseline") if isinstance(binding.get("baseline"), Mapping) else {} - baseline_identity = semantic_digest(dict(baseline)) + baseline_identity = _accepted_baseline_identity(binding) authority_projection = _accepted_authority_projection( task, binding, - review_id=review_id, + accepted_review=review, owner_identity=accepted_ownership, ) evidence = capture_repository_evidence(Path(str(binding.get("execution_path") or "")).resolve()) @@ -1643,14 +1648,17 @@ def assert_accepted_task_result_current( raise SystemExit("accepted task result authority projection is not closed") if not isinstance(owner_identity, Mapping): raise SystemExit("accepted task result owner identity is invalid") - current_projection = _accepted_authority_projection( - task, - binding, - review_id=str(accepted.get("review_id") or ""), - owner_identity=owner_identity, - ) + current_projection = { + "task_digest": semantic_digest(_accepted_task_projection(task)), + "binding_digest": semantic_digest(_accepted_binding_projection(binding)), + "scope_digest": semantic_digest(_canonical_task_scopes(task)), + "validation_obligations_digest": semantic_digest(_accepted_validation_projection(task)), + "ownership_digest": semantic_digest( + _accepted_ownership_projection(task, binding, owner_identity) + ), + } binding_ownership = binding.get("ownership") if isinstance(binding.get("ownership"), Mapping) else {} - baseline = binding.get("baseline") if isinstance(binding.get("baseline"), Mapping) else {} + baseline_identity = _accepted_baseline_identity(binding) checks = { "plan": str(task.get("plan_id") or "") == accepted.get("plan_id"), "task": ( @@ -1659,18 +1667,22 @@ def assert_accepted_task_result_current( ), "binding": ( str(binding_ownership.get("binding_id") or "") == accepted.get("binding_id") - and semantic_digest(dict(baseline)) == accepted.get("baseline_identity") + and baseline_identity == accepted.get("baseline_identity") and current_projection["binding_digest"] == authority_projection.get("binding_digest") ), "scope": current_projection["scope_digest"] == authority_projection.get("scope_digest"), "validation": current_projection["validation_obligations_digest"] == authority_projection.get("validation_obligations_digest"), - "review": current_projection["required_review_digest"] == authority_projection.get("required_review_digest"), + "review": (task.get("review_required") is True) == bool(accepted.get("review_id")), "ownership": current_projection["ownership_digest"] == authority_projection.get("ownership_digest"), "source": accepted_source.get("state_digest") == _accepted_source_state_digest( plan_id=str(accepted.get("plan_id") or ""), task_id=str(accepted.get("task_id") or ""), binding_id=str(accepted.get("binding_id") or ""), - baseline_identity=str(accepted.get("baseline_identity") or ""), + baseline_identity=( + accepted.get("baseline_identity") + if isinstance(accepted.get("baseline_identity"), Mapping) + else {} + ), head=accepted_source.get("head"), tree=accepted_source.get("tree"), authority_projection=authority_projection, diff --git a/tests/test_wor109_accepted_result.py b/tests/test_wor109_accepted_result.py index 9f88556..4fdc936 100644 --- a/tests/test_wor109_accepted_result.py +++ b/tests/test_wor109_accepted_result.py @@ -182,6 +182,17 @@ def test_accepted_result_is_deterministic_current_authority_not_handoff_history( "ownership_digest", } assert set(first["accepted_source"]) == {"head", "tree", "state_digest"} + assert first["baseline_identity"] == {"head": OID_A, "tree": OID_B} + assert first["accepted_source"]["state_digest"] == execution_context.semantic_digest( + { + "plan_id": first["plan_id"], + "task_id": first["task_id"], + "binding_id": first["binding_id"], + "baseline_identity": {"head": OID_A, "tree": OID_B}, + "accepted_source": {"head": OID_A, "tree": OID_B}, + "authority_projection": first["authority_projection"], + } + ) assert first["validation_evidence_ids"] == ["obs-001"] assert "mutation_events" not in repr(first) assert "validation" not in first["executor_result_digest"] @@ -213,6 +224,49 @@ def test_accepted_result_is_deterministic_current_authority_not_handoff_history( execution_context.assert_accepted_task_result_current(task, binding, invalidated) +def test_actual_accepted_repair_review_mode_and_frontier_are_digest_authority( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + monkeypatch.setattr( + execution_context, + "capture_repository_evidence", + lambda _root: {"head": OID_A, "tree": OID_B, "entries": {}, "status": "dirty"}, + ) + task = _task(tmp_path) + binding = _binding(tmp_path) + initial = execution_context.build_accepted_task_result( + task, binding, _handoff(), _validated(), accepted_at="2026-09-06T10:00:00Z" + ) + repaired_handoff = deepcopy(_handoff()) + repaired_handoff["acceptance_review"].update( + { + "review_mode": "repair", + "repair_frontier": { + "prior_review_id": "review-prior", + "frozen_evidence_reference": "evidence-001", + }, + } + ) + repaired = execution_context.build_accepted_task_result( + task, binding, repaired_handoff, _validated(), accepted_at="2026-09-06T10:00:00Z" + ) + + assert ( + initial["authority_projection"]["required_review_digest"] + != repaired["authority_projection"]["required_review_digest"] + ) + assert repaired["authority_projection"]["required_review_digest"] == execution_context.semantic_digest( + { + "required": True, + "review_id": "review-001", + "verdict": "accepted", + "review_mode": "repair", + "repair_frontier": repaired_handoff["acceptance_review"]["repair_frontier"], + } + ) + execution_context.assert_accepted_task_result_current(task, binding, repaired) + + def test_unrelated_repository_advance_does_not_stale_accepted_task_result( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: From 5c2579b016ef4d1f137080caa2ca309f265e072d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 07:11:17 +0800 Subject: [PATCH 04/48] feat(orchestration): consume accepted task authority --- .../components/native-transition-record.yaml | 3 + scripts/orchestration/dispatcher.py | 32 +-- scripts/orchestration/execution_context.py | 71 ++++++ scripts/orchestration/plans.py | 144 +++++++++++- tests/test_wor108_context_projection.py | 75 +----- tests/test_wor109_lifecycle.py | 221 ++++++++++++++++++ 6 files changed, 444 insertions(+), 102 deletions(-) create mode 100644 tests/test_wor109_lifecycle.py diff --git a/evals/wor105/components/native-transition-record.yaml b/evals/wor105/components/native-transition-record.yaml index 18c1e41..3380f67 100644 --- a/evals/wor105/components/native-transition-record.yaml +++ b/evals/wor105/components/native-transition-record.yaml @@ -7,6 +7,9 @@ review_path: .work-bundle/orchestration/reviews/WOR-105-task-b06r-kernel-review- review_sha256: d638b8959b4db57dd33e072b01428d6ce89f65a9771c05eff26d8e40d4293ebd accepted_commit: 9dce5df221485174d6179f713e8b179bbc20567a accepted_tree: 5a1f38355eae8068bab528923e807ce54e6f6fe5 +release_anchor: + commit: cfa089f0d2ed211b98d049eb37bfcdccb8091516 + tree: 12e4a696c3caf991654f0b9ac9ef40594699c8d4 integrated_validation: {id: VAL-B06R-TEST, result: passed, tests: 300} handoff_validation: {id: VAL-B06R-IDENTITY, result: passed, adversarial_cases: 5} participant_handoffs: diff --git a/scripts/orchestration/dispatcher.py b/scripts/orchestration/dispatcher.py index 14c200c..8d665da 100644 --- a/scripts/orchestration/dispatcher.py +++ b/scripts/orchestration/dispatcher.py @@ -10,8 +10,6 @@ from execution_context import ( cmd_build_review_package, cmd_build_task_brief, - cmd_adopt_existing_recovered_result, - cmd_create_accepted_base_absence_receipt, cmd_observe_task_validation, cmd_validate_executor_result, ) @@ -24,8 +22,7 @@ RECOGNIZED_COMMANDS = frozenset({ "init", "doctor", "state", "next-action-candidates", "git-status", "repository-preflight", "build-task-brief", "build-review-package", - "validate-executor-result", "observe-task-validation", "create-accepted-base-absence-receipt", - "adopt-existing-recovered-result", + "validate-executor-result", "observe-task-validation", "related", "write-doc", "write-spec", "list-specs", "set-spec-status", "index-specs", "write-plan", "list-plans", "set-plan-status", "archive-plan", "index-plans", "write-phase", "write-task", @@ -92,33 +89,6 @@ def build_parser() -> argparse.ArgumentParser: observe_validation = sub.add_parser("observe-task-validation", parents=[parent]) observe_validation.add_argument("--task", required=True) observe_validation.set_defaults(func=cmd_observe_task_validation) - create_recovery_receipt = sub.add_parser( - "create-accepted-base-absence-receipt", parents=[parent] - ) - create_recovery_receipt.add_argument("--plan-id", required=True) - create_recovery_receipt.add_argument("--task-id", required=True) - create_recovery_receipt.add_argument("--expected-head", required=True) - create_recovery_receipt.add_argument("--expected-tree", required=True) - create_recovery_receipt.add_argument("--proposed-handoff-id", required=True) - create_recovery_receipt.add_argument("--proposed-review-id", required=True) - create_recovery_receipt.add_argument("--final-head", required=True) - create_recovery_receipt.add_argument("--final-tree", required=True) - create_recovery_receipt.set_defaults(func=cmd_create_accepted_base_absence_receipt) - adopt_recovered_result = sub.add_parser( - "adopt-existing-recovered-result", parents=[parent] - ) - adopt_recovered_result.add_argument("--plan-id", required=True) - adopt_recovered_result.add_argument("--task-id", required=True) - adopt_recovered_result.add_argument("--expected-head", required=True) - adopt_recovered_result.add_argument("--expected-tree", required=True) - adopt_recovered_result.add_argument("--handoff-id", required=True) - adopt_recovered_result.add_argument("--handoff-sha256", required=True) - adopt_recovered_result.add_argument("--review-id", required=True) - adopt_recovered_result.add_argument("--final-head", required=True) - adopt_recovered_result.add_argument("--final-tree", required=True) - adopt_recovered_result.add_argument("--prior-receipt-id", required=True) - adopt_recovered_result.add_argument("--prior-receipt-sha256", required=True) - adopt_recovered_result.set_defaults(func=cmd_adopt_existing_recovered_result) related = sub.add_parser("related", parents=[parent]) related.add_argument("--id", required=True) related.set_defaults(func=cmd_related) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index b283698..bf343f8 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -1712,6 +1712,34 @@ def materialize_accepted_task_result( return accepted +def load_current_accepted_task_result( + control_root: Path, task: Mapping[str, Any] +) -> tuple[dict[str, Any], dict[str, Any]]: + """Load compact post-acceptance authority without replaying its evidence history.""" + + root = control_root.expanduser().resolve() + binding = load_task_execution_binding( + root, str(task.get("plan_id") or ""), str(task.get("task_id") or "") + ) + accepted = binding.get("accepted_result") + if not isinstance(accepted, Mapping): + raise SystemExit("accepted task result is missing") + assert_accepted_task_result_current(task, binding, accepted) + return binding, dict(accepted) + + +def has_persisted_accepted_task_result( + control_root: Path, plan_id: str, task_id: str +) -> bool: + """Detect the irreversible accepted-result lifecycle without validating history.""" + + path = _binding_path(control_root.expanduser().resolve(), plan_id, task_id) + if not path.exists(): + return False + binding = _read_binding_file(path) + return isinstance(binding.get("accepted_result"), Mapping) + + def capture_task_baseline_once(binding: dict[str, Any], control_root: Path | None = None) -> dict[str, Any]: existing = binding.get("baseline") if isinstance(existing, dict) and existing.get("head"): @@ -2977,6 +3005,49 @@ def _accepted_dependency_paths( ) -> set[str]: descriptors = list(accepted_dependency_deltas or []) if not descriptors: + dependencies = {str(value) for value in _as_list(task.get("depends_on"))} + if not dependencies: + return set() + workspace = task.get("workspace") if isinstance(task.get("workspace"), dict) else {} + control_root = Path(str(workspace.get("root") or "")).resolve() + plan_id = str(task.get("plan_id") or "") + for dependency_id in sorted(dependencies): + matches: list[Path] = [] + plan_root = control_root / ".work-bundle/orchestration/plan" + for status in ("active", "archived"): + for path in sorted((plan_root / status).glob("**/*.md")): + try: + document, _ = _read_structured(path) + except (OSError, SystemExit, ValueError): + continue + if ( + str(document.get("id") or "") == dependency_id + and str(document.get("plan_id") or "") == plan_id + ): + matches.append(path) + if len(matches) != 1: + raise SystemExit( + f"accepted dependency task authority is missing or ambiguous: {dependency_id}" + ) + compile_args = argparse.Namespace( + project_root=str(control_root), + workspace_root=str(control_root), + task=str(matches[0]), + handoff=None, + base=None, + head=None, + workspace_id=None, + execution_id=None, + repository_id=None, + execution_runtime_root=None, + mutation_events=None, + accepted_dependency_deltas=None, + prior_ownership=None, + repair_continuity=None, + authorized_replacements=None, + ) + _, brief_document = _compile_task_brief(compile_args) + load_current_accepted_task_result(control_root, brief_document["task_brief"]) return set() cumulative_fields = { "task_id", "accepted_result_base", "review_chain", "integrated_base", "integrated_head" diff --git a/scripts/orchestration/plans.py b/scripts/orchestration/plans.py index 21cdaef..77fb513 100644 --- a/scripts/orchestration/plans.py +++ b/scripts/orchestration/plans.py @@ -1,4 +1,3 @@ -import hashlib import subprocess from datetime import datetime, timezone @@ -16,7 +15,10 @@ _parse_scalar, _execution_workspace_module, _persist_binding, + has_persisted_accepted_task_result, load_task_execution_binding, + load_current_accepted_task_result, + semantic_digest, ) from completion_provenance import ManagedProvenanceStore, release_completion_binding from handoffs import _read_compact_yaml_metadata @@ -535,7 +537,117 @@ def cmd_list_plans(args: argparse.Namespace) -> None: print(json.dumps(row, ensure_ascii=False)) -def _assert_completed_task_handoff(args: argparse.Namespace, task_path: Path) -> None: +def _load_current_task_acceptance( + args: argparse.Namespace, task_path: Path +) -> tuple[dict[str, object], dict[str, object]]: + compile_args = argparse.Namespace( + project_root=getattr(args, "project_root", None), + workspace_root=getattr(args, "workspace_root", None), + task=str(task_path), + handoff=None, + base=None, + head=None, + **_observation_kwargs(args), + ) + _, brief_document = _compile_task_brief(compile_args) + return load_current_accepted_task_result( + resolve_workspace_root(args), brief_document["task_brief"] + ) + + +def _task_brief_at(args: argparse.Namespace, task_path: Path) -> dict[str, object]: + compile_args = argparse.Namespace( + project_root=getattr(args, "project_root", None), + workspace_root=getattr(args, "workspace_root", None), + task=str(task_path), + handoff=None, + base=None, + head=None, + **_observation_kwargs(args), + ) + _, brief_document = _compile_task_brief(compile_args) + return brief_document["task_brief"] + + +def _assert_task_dependencies_current(args: argparse.Namespace, task_path: Path) -> None: + front_matter, _body = read_front_matter(task_path) + if not front_matter.get("depends_on"): + return + brief = _task_brief_at(args, task_path) + rows = index_plans(args) + for dependency_id in brief.get("depends_on", []): + matches = [ + row for row in rows + if row.get("type") == "task" + and row.get("plan_id") == brief.get("plan_id") + and row.get("id") == dependency_id + ] + if len(matches) != 1 or matches[0].get("status") != "Completed": + raise SystemExit(f"dependency-blocked: {dependency_id} is not completed") + _load_current_task_acceptance(args, artifact_path_from_row(matches[0], args)) + + +def _accepted_plan_task_results( + args: argparse.Namespace, plan_id: str +) -> list[tuple[dict[str, object], dict[str, object]]]: + accepted: list[tuple[dict[str, object], dict[str, object]]] = [] + for row in index_plans(args): + if row.get("type") != "task" or row.get("plan_id") != plan_id: + continue + if row.get("status") != "Completed": + raise SystemExit(f"acceptance-blocked: task {row.get('id')} is not completed") + path = artifact_path_from_row(row, args) + _binding, result = _load_current_task_acceptance(args, path) + accepted.append((result, _task_brief_at(args, path))) + return accepted + + +def _plan_uses_accepted_result_authority(args: argparse.Namespace, plan_id: str) -> bool: + control_root = resolve_workspace_root(args) + for row in index_plans(args): + if row.get("type") != "task" or row.get("plan_id") != plan_id: + continue + if has_persisted_accepted_task_result( + control_root, plan_id, str(row.get("id") or "") + ): + return True + return False + + +def _assert_phase_tasks_accepted(args: argparse.Namespace, phase_id: str, plan_id: str) -> None: + rows = [ + row for row in index_plans(args) + if row.get("type") == "task" + and row.get("plan_id") == plan_id + and row.get("phase_id") == phase_id + ] + for row in rows: + if row.get("status") != "Completed": + raise SystemExit(f"acceptance-blocked: task {row.get('id')} is not completed") + _load_current_task_acceptance(args, artifact_path_from_row(row, args)) + + +def _assert_completed_task_authority( + args: argparse.Namespace, task_path: Path +) -> dict[str, object]: + try: + _binding, accepted = _load_current_task_acceptance(args, task_path) + return accepted + except SystemExit as error: + missing_initial_binding = str(error) == "Task execution binding is missing harness provenance" + if missing_initial_binding: + front_matter, _body = read_front_matter(task_path) + published = has_persisted_accepted_task_result( + resolve_workspace_root(args), + str(front_matter.get("plan_id") or ""), + str(front_matter.get("id") or ""), + ) + else: + published = False + if str(error) != "accepted task result is missing" and not ( + missing_initial_binding and not published + ): + raise handoff = getattr(args, "handoff", None) if not handoff: raise SystemExit("set-plan-status Completed for a task requires --handoff") @@ -550,6 +662,8 @@ def _assert_completed_task_handoff(args: argparse.Namespace, task_path: Path) -> **_observation_kwargs(args), ) ) + _binding, accepted = _load_current_task_acceptance(args, task_path) + return accepted def _release_completed_task_binding(args: argparse.Namespace, row: dict[str, object]) -> dict[str, object]: @@ -559,8 +673,8 @@ def _release_completed_task_binding(args: argparse.Namespace, row: dict[str, obj plan_id = str(row["plan_id"]) task_id = str(row["id"]) binding = load_task_execution_binding(control_root, plan_id, task_id) - handoff = Path(str(getattr(args, "handoff", ""))) - artifact_digest = hashlib.sha256(handoff.read_bytes()).hexdigest() if handoff.is_file() else None + accepted = binding.get("accepted_result") if isinstance(binding.get("accepted_result"), dict) else {} + artifact_digest = semantic_digest(accepted) if accepted else None event = { "event_id": "event-template", "timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), @@ -581,7 +695,11 @@ def _release_completed_task_binding(args: argparse.Namespace, row: dict[str, obj "finding_class": None, "return_reason": "validated completion", "owner": task_id, - "identity": {"product_tree": None, "artifact_digest": artifact_digest, "mutation_epoch": 0}, + "identity": { + "product_tree": (accepted.get("accepted_source") or {}).get("tree"), + "artifact_digest": artifact_digest, + "mutation_epoch": 0, + }, "privacy": "operational_metadata_only", } store = ManagedProvenanceStore(control_root / ".work-bundle/runtime/completion-provenance") @@ -630,11 +748,17 @@ def cmd_set_plan_status(args: argparse.Namespace) -> None: raise SystemExit(f"Multiple plan artifacts match {args.id}{guidance}") row = matches[0] path = artifact_path_from_row(row, args) + if row.get("type") == "task" and args.status in {"In progress", "Completed"}: + _assert_task_dependencies_current(args, path) + if row.get("type") == "phase" and args.status == "Completed": + _assert_phase_tasks_accepted(args, str(row["id"]), str(row["plan_id"])) if row.get("type") == "plan" and args.status in {"In progress", "Completed"}: require_plan_reviews(project_root(args), path, source_root=_resolve_final_plan_workspace(args) if args.status == "Completed" else None) + if args.status == "Completed": + _accepted_plan_task_results(args, str(row["id"])) if args.status == "Completed" and row.get("type") == "task": - _assert_completed_task_handoff(args, path) + _assert_completed_task_authority(args, path) _release_completed_task_binding(args, row) replace_front_matter_value(path, "status", args.status) if args.status == "Deprecated": @@ -658,7 +782,13 @@ def cmd_archive_plan(args: argparse.Namespace) -> None: root_path = artifact_path_from_row(root_match, args) require_plan_reviews(project_root(args), root_path, source_root=_resolve_final_plan_workspace(args)) - validated = _validated_plan_task_handoffs(args, args.id) + if _plan_uses_accepted_result_authority(args, args.id): + _accepted_plan_task_results(args, args.id) + validated: list[tuple[dict[str, object], dict[str, object]]] = [] + else: + # Pre-accepted-result plans retain a bounded migration path. New plans + # switch irreversibly once any task publishes durable accepted authority. + validated = _validated_plan_task_handoffs(args, args.id) _assert_archive_knowledge_gate(args, args.id, root_path, validated) _assert_archive_plan_acceptance(args, args.id, root_path, validated) require_plan_reviews(project_root(args), root_path, source_root=_resolve_final_plan_workspace(args)) diff --git a/tests/test_wor108_context_projection.py b/tests/test_wor108_context_projection.py index 16a1546..7b99f6f 100644 --- a/tests/test_wor108_context_projection.py +++ b/tests/test_wor108_context_projection.py @@ -534,7 +534,7 @@ def test_accepted_dependency_deltas_use_exact_handoff_and_observed_checkpoint( }, } - with pytest.raises(SystemExit, match="workflow.md"): + with pytest.raises(SystemExit, match="accepted dependency task authority"): execution_context.validate_executor_result_for_task( handoff, current, observe=True, mutation_events=[] ) @@ -1129,35 +1129,17 @@ def reviewer(agent_id: str) -> dict[str, object]: recovered_path = handoff_dir / "handoff-recovered-dependency.yaml" index = root / ".work-bundle/orchestration/handoff/index.jsonl" index.write_text("", encoding="utf-8") - create_receipt = subprocess.run( - [ - sys.executable, - str(REPO_ROOT / "scripts/orch.py"), - "create-accepted-base-absence-receipt", - "--project-root", - str(root), - "--plan-id", - "plan-001", - "--task-id", - "task-dependency", - "--expected-head", - expected_base_head, - "--expected-tree", - expected_base_tree, - "--proposed-handoff-id", - recovered_handoff["id"], - "--proposed-review-id", - recovered_review["review_id"], - "--final-head", - recovered_head, - "--final-tree", - recovered_tree, - ], - capture_output=True, - text=True, + receipt_reference = execution_context.create_accepted_base_absence_receipt( + root, + "plan-001", + "task-dependency", + expected_base_head, + expected_base_tree, + recovered_handoff["id"], + recovered_review["review_id"], + recovered_head, + recovered_tree, ) - assert create_receipt.returncode == 0, create_receipt.stderr - receipt_reference = json.loads(create_receipt.stdout) recovered_path.write_text( ("\n".join(execution_context._dump_yaml(recovered_handoff)) + "\n").replace( ": none\n", ': "none"\n' @@ -1300,41 +1282,6 @@ def reviewer(agent_id: str) -> dict[str, object]: dependent, root, [adopted_descriptor] ) == {dependency_path} - adopt_cli = subprocess.run( - [ - sys.executable, - str(REPO_ROOT / "scripts/orch.py"), - "adopt-existing-recovered-result", - "--project-root", - str(root), - "--plan-id", - "plan-001", - "--task-id", - "task-dependency", - "--expected-head", - expected_base_head, - "--expected-tree", - expected_base_tree, - "--handoff-id", - recovered_handoff["id"], - "--handoff-sha256", - recovered_reference["handoff_sha256"], - "--review-id", - recovered_review["review_id"], - "--final-head", - recovered_head, - "--final-tree", - recovered_tree, - "--prior-receipt-id", - legacy_reference["receipt_id"], - "--prior-receipt-sha256", - legacy_reference["receipt_sha256"], - ], - capture_output=True, - text=True, - ) - assert adopt_cli.returncode == 0, adopt_cli.stderr - assert set(json.loads(adopt_cli.stdout)) == {"receipt_id", "receipt_sha256"} assert recovered_path.read_bytes() == handoff_bytes assert index.read_bytes() == index_bytes diff --git a/tests/test_wor109_lifecycle.py b/tests/test_wor109_lifecycle.py new file mode 100644 index 0000000..c0a8650 --- /dev/null +++ b/tests/test_wor109_lifecycle.py @@ -0,0 +1,221 @@ +from __future__ import annotations + +import argparse +import importlib.util +import json +from pathlib import Path +import sys + +import pytest + + +REPO_ROOT = Path(__file__).resolve().parents[1] +ORCHESTRATION = REPO_ROOT / "scripts" / "orchestration" +sys.path.insert(0, str(ORCHESTRATION)) + +import execution_context # noqa: E402 +import plans # noqa: E402 +from test_wor109_accepted_result import _binding, _handoff, _task, _validated # noqa: E402 + + +def _dispatcher(): + spec = importlib.util.spec_from_file_location( + "wor109_dispatcher", ORCHESTRATION / "dispatcher.py" + ) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def test_current_accepted_result_does_not_read_handoff_or_replay_validation( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + task = _task(tmp_path) + binding = _binding(tmp_path) + monkeypatch.setattr( + execution_context, + "capture_repository_evidence", + lambda _root: {"head": "a" * 40, "tree": "b" * 40, "entries": {}, "status": "clean"}, + ) + accepted = execution_context.build_accepted_task_result( + task, binding, _handoff(), _validated(), accepted_at="2026-09-07T00:00:00Z" + ) + binding["accepted_result"] = accepted + monkeypatch.setattr(execution_context, "load_task_execution_binding", lambda *_args: binding) + + current_binding, current = execution_context.load_current_accepted_task_result( + tmp_path, task + ) + + assert current_binding is binding + assert current == accepted + + +def test_task_completion_reuses_current_accepted_result_without_handoff( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + task_path = tmp_path / "task.md" + task_path.write_text("---\nid: task-001\nplan_id: plan-001\n---\n", encoding="utf-8") + accepted = {"schema": "accepted-task-result-v1"} + calls: list[str] = [] + monkeypatch.setattr(plans, "_load_current_task_acceptance", lambda *_args: ({}, accepted)) + monkeypatch.setattr( + plans, + "cmd_validate_executor_result", + lambda _args: calls.append("replayed"), + ) + + assert plans._assert_completed_task_authority(argparse.Namespace(), task_path) == accepted + assert calls == [] + + +def test_task_completion_does_not_replay_when_persisted_authority_is_stale( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + task_path = tmp_path / "task.md" + task_path.write_text("---\nid: task-001\nplan_id: plan-001\n---\n", encoding="utf-8") + calls: list[str] = [] + monkeypatch.setattr( + plans, + "_load_current_task_acceptance", + lambda *_args: (_ for _ in ()).throw( + SystemExit("accepted task result is stale: scope authority changed") + ), + ) + monkeypatch.setattr( + plans, "cmd_validate_executor_result", lambda _args: calls.append("replayed") + ) + + with pytest.raises(SystemExit, match="stale: scope"): + plans._assert_completed_task_authority( + argparse.Namespace(handoff="historical.yaml"), task_path + ) + assert calls == [] + + +def test_dependency_and_phase_gates_consume_only_current_accepted_results( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + task_path = tmp_path / "task.md" + task_path.write_text( + "---\nid: task-002\nplan_id: plan-001\nphase_id: phase-001\n" + "depends_on: [task-001]\n---\n", + encoding="utf-8", + ) + rows = [ + { + "type": "task", + "id": "task-001", + "plan_id": "plan-001", + "phase_id": "phase-001", + "status": "Completed", + "path": "dependency.md", + }, + { + "type": "task", + "id": "task-002", + "plan_id": "plan-001", + "phase_id": "phase-001", + "status": "Completed", + "path": "task.md", + }, + ] + loaded: list[str] = [] + monkeypatch.setattr(plans, "index_plans", lambda _args: rows) + monkeypatch.setattr( + plans, + "_task_brief_at", + lambda _args, path: { + "plan_id": "plan-001", + "task_id": "task-002" if path.name == "task.md" else "task-001", + "depends_on": ["task-001"] if path.name == "task.md" else [], + }, + ) + monkeypatch.setattr( + plans, + "artifact_path_from_row", + lambda row, _args: tmp_path / str(row["path"]), + ) + monkeypatch.setattr( + plans, + "_load_current_task_acceptance", + lambda _args, path: (loaded.append(path.name), ({}, {"schema": "accepted-task-result-v1"}))[1], + ) + + plans._assert_task_dependencies_current(argparse.Namespace(), task_path) + plans._assert_phase_tasks_accepted( + argparse.Namespace(), "phase-001", "plan-001" + ) + + assert loaded == ["dependency.md", "dependency.md", "task.md"] + + +def test_archive_switches_irreversibly_to_accepted_results_without_handoff_replay( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + plan_root = tmp_path / ".work-bundle/orchestration/plan/active" + task_root = plan_root / "plan" / "phase-001" + task_root.mkdir(parents=True) + (plan_root / "plan.md").write_text( + "---\nid: plan-001\nstatus: Completed\n---\n", encoding="utf-8" + ) + (task_root / "task.md").write_text( + "---\nid: task-001\nplan_id: plan-001\nphase_id: phase-001\n" + "status: Completed\n---\n", + encoding="utf-8", + ) + binding_path = ( + tmp_path + / ".work-bundle/runtime/execution/plan-001/task-001/execution-binding.json" + ) + binding_path.parent.mkdir(parents=True) + binding_path.write_text( + json.dumps({"accepted_result": {"schema": "accepted-task-result-v1"}}), + encoding="utf-8", + ) + calls: list[str] = [] + monkeypatch.setattr(plans, "require_plan_reviews", lambda *_args, **_kwargs: None) + monkeypatch.setattr( + plans, + "_accepted_plan_task_results", + lambda *_args: calls.append("accepted") or [], + ) + monkeypatch.setattr( + plans, + "_validated_plan_task_handoffs", + lambda *_args: (_ for _ in ()).throw(AssertionError("handoff replayed")), + ) + monkeypatch.setattr(plans, "_assert_archive_knowledge_gate", lambda *_args: None) + monkeypatch.setattr(plans, "_assert_archive_plan_acceptance", lambda *_args: None) + + plans.cmd_archive_plan(argparse.Namespace(project_root=str(tmp_path), id="plan-001")) + + assert calls == ["accepted"] + assert (tmp_path / ".work-bundle/orchestration/plan/archived/plan.md").is_file() + + +def test_recovery_commands_are_not_public_dispatcher_actions() -> None: + dispatcher = _dispatcher() + + assert "create-accepted-base-absence-receipt" not in dispatcher.RECOGNIZED_COMMANDS + assert "adopt-existing-recovered-result" not in dispatcher.RECOGNIZED_COMMANDS + with pytest.raises(SystemExit): + dispatcher.build_parser().parse_args(["create-accepted-base-absence-receipt"]) + + +def test_wor105_historical_identity_and_release_anchor_are_separate() -> None: + import yaml + + record = yaml.safe_load( + (REPO_ROOT / "evals/wor105/components/native-transition-record.yaml").read_text( + encoding="utf-8" + ) + ) + + assert record["accepted_commit"] == "9dce5df221485174d6179f713e8b179bbc20567a" + assert record["accepted_tree"] == "5a1f38355eae8068bab528923e807ce54e6f6fe5" + assert record["release_anchor"] == { + "commit": "cfa089f0d2ed211b98d049eb37bfcdccb8091516", + "tree": "12e4a696c3caf991654f0b9ac9ef40594699c8d4", + } From 75cec40c38c646faa13c5adfda9e57c1641b4f6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 07:21:55 +0800 Subject: [PATCH 05/48] fix(orchestration): stabilize planner decomposition --- .../assets/orchestration/contract/plan-v1.md | 6 +- references/assets/orchestration/workflow.md | 4 +- references/evals/orchestration/evals.json | 54 +++++++++++++++ .../orchestration/orch-artifact-authoring.md | 9 ++- .../orch-create-implementation-plan/SKILL.md | 5 +- skills/orch-create-specification/SKILL.md | 2 +- .../test_orchestration_skill_rule_boundary.py | 4 +- .../test_orchestration_workflow_contracts.py | 2 +- tests/test_wor109_planner_contracts.py | 68 +++++++++++++++++++ 9 files changed, 142 insertions(+), 12 deletions(-) create mode 100644 tests/test_wor109_planner_contracts.py diff --git a/references/assets/orchestration/contract/plan-v1.md b/references/assets/orchestration/contract/plan-v1.md index 60f589d..e4a7cb2 100644 --- a/references/assets/orchestration/contract/plan-v1.md +++ b/references/assets/orchestration/contract/plan-v1.md @@ -83,7 +83,9 @@ Use a compact source-spec ID map. Do not paste long specification sections into ## 3.1 Compactness Check -Plans use the minimum orchestration overhead that preserves complete requirement coverage, Truth Basis continuity, independently falsifiable and testable increments, short evidence loops, exact dependencies, disjoint write scopes, validation ownership, bounded failure radius, handoff requirements, and review boundaries. Do not split one mechanical increment when it already satisfies those constraints, and do not split phases or tasks only to mirror template sections, lifecycle labels, file count, or repeated prose. +Plans do not optimize task or phase cardinality. Bound expected total orchestration cost by decomposing only at concrete independently owned production, dependency, validation, review, and repair seams while preserving complete requirement coverage and Truth Basis continuity. Assign every authoritative production path to a production owner; helper-only allocation cannot leave its production path unowned. Keep a coherent mechanical increment with one owner, oracle, and repair frontier together. Create a phase only for an actual barrier or convergence boundary, and reject speculative splits unsupported by current authority, repository, dependency, ownership, validation, or acceptance evidence. + +When execution proves a task materially under-decomposed, return to the plan and reslice only the affected region around the evidenced seam. Preserve the original binding, baseline, and accepted unaffected regions; do not repeatedly enlarge the task. Every executable task declares the same five-field Truth Basis. When a consequential simplification or compatibility assumption exists, make the earliest ordinary task cheaply falsify it before broad edits. Do not add a risk score, checkpoint phase, or parallel lifecycle. @@ -144,7 +146,7 @@ How to make tasks parallel: create or confirm a stable boundary artifact before | VERIFY-003 | Safe parallelization is exposed where dependencies and write scopes allow, and unsafe parallelization is explicitly blocked by dependency or scope evidence. | plan/phase/task | passed|repaired|blocked | [Same-turn repair or source-spec repair blocker.] | | VERIFY-004 | Every task, phase, and plan completion path requires `create-handoff` with a compact, sparse YAML `executor-result` handoff whose body stays applicability-based. | plan/phase/task | passed|repaired|blocked | [Same-turn repair or source-spec repair blocker.] | | VERIFY-005 | `allocated_rules` and `allocated_skills` cover all material rule/skill conditions from the source specification, affected files, operation type, CodeGraph/Git needs, validation tasks, and any non-WorkBundle rule/skill sources already visible to the agent. | plan/phase/task | passed|repaired|blocked | [Same-turn repair or source-spec repair blocker.] | -| VERIFY-006 | Compactness, contract group clarity, barrier correctness, co-worker isolation, convergence validation, and contract-only handoff criteria are present where parallel branches share a contract. | plan/phase/task | passed|repaired|blocked | [Same-turn repair or source-spec repair blocker.] | +| VERIFY-006 | Review-stable decomposition, production-path ownership, repair-frontier locality, contract group clarity, actual barrier correctness, co-worker isolation, convergence validation, and contract-only handoff criteria are present where applicable. | plan/phase/task | passed|repaired|blocked | [Same-turn repair or source-spec repair blocker.] | | VERIFY-007 | Each task carries allocated Truth Basis authority and the earliest ordinary task falsifies consequential assumptions before broad simplification. | plan/phase/task | passed|repaired|blocked | [Same-turn repair or source-spec repair blocker.] | If any generated artifact drifts from the source specification, omits required spec-ID coverage, contains inconsistent paths or dependencies, lacks validation, lacks allocated rule/skill coverage, or lacks handoff criteria, repair the generated artifacts in the same planning turn and repeat this verification. If the source specification itself has unresolved questions, missing stable IDs, missing evidence, or contradictory instructions, stop for specification repair instead of inventing plan content. diff --git a/references/assets/orchestration/workflow.md b/references/assets/orchestration/workflow.md index 82df93d..723219a 100644 --- a/references/assets/orchestration/workflow.md +++ b/references/assets/orchestration/workflow.md @@ -37,7 +37,9 @@ execution_workspace: Specification creation decides policy only; it does not provision a worktree. Planning carries that policy into executable tasks. -Plans keep durable artifacts normalized and DRY. Every executable task cites source IDs, the accepted Truth Basis, exact file scope, dependencies, validation, methodology, allocated rules/skills, a provider-neutral executor profile, and acceptance-review requirements. Decomposition uses minimum orchestration overhead while preserving Truth Basis continuity, independently falsifiable and testable increments, short evidence loops, dependencies, disjoint write scopes, validation ownership, bounded failure radius, and review boundaries; one sound mechanical increment is not split merely to minimize size. When simplification depends on a consequential assumption, the earliest ordinary task cheaply falsifies it before broad edits; do not add a checkpoint phase or risk-score lifecycle. Contract-decoupled parallel tasks share a stable contract group, validate only against that contract plus accepted handoffs and task-local files, reach a named barrier, and defer joint checks to the convergence owner. +Plans keep durable artifacts normalized and DRY. Every executable task cites source IDs, the accepted Truth Basis, exact file scope, dependencies, validation, methodology, allocated rules/skills, a provider-neutral executor profile, and acceptance-review requirements. Decomposition does not optimize task or phase cardinality: it bounds expected total orchestration cost with concrete independently owned production, dependency, validation, review, and repair seams while preserving exact dependencies and disjoint write scopes. Every authoritative production path has a production owner; helper-only allocation cannot leave the path unowned. A coherent mechanical increment with one owner, oracle, and repair frontier stays together. A phase exists only for an actual barrier or convergence boundary, and speculative splits unsupported by current authority, repository, dependency, ownership, validation, or acceptance evidence are rejected. When simplification depends on a consequential assumption, the earliest ordinary task cheaply falsifies it before broad edits; do not add a checkpoint phase or risk-score lifecycle. Contract-decoupled parallel tasks share a stable contract group, validate only against that contract plus accepted handoffs and task-local files, reach a named barrier, and defer joint checks to the convergence owner. + +When execution proves a task materially under-decomposed, return to the plan and reslice only the affected region at the newly evidenced seam. Preserve the original binding, baseline, and accepted unaffected regions; do not repeatedly enlarge the task or create a parallel retry lifecycle. Generated specifications and plans record compact semantic convergence evidence: diff --git a/references/evals/orchestration/evals.json b/references/evals/orchestration/evals.json index bd1d30f..e9b643d 100644 --- a/references/evals/orchestration/evals.json +++ b/references/evals/orchestration/evals.json @@ -522,6 +522,60 @@ "prompt": "A planner allocates source_ids: [EXC-001] for a deferred excellence proposal while compiling an implementation plan.", "expected_output": "Rejects EXC-* proposal IDs as non-authoritative; planning and the compiled brief use only the stable requirement, constraint, interface, acceptance-criterion, or validation-target IDs that an accepted proposal projected to.", "files": [] + }, + { + "id": "PD-01", + "prompt": "Plan a change whose production seams support six tasks, while a reviewer proposes a three-task target to make the plan shorter.", + "expected_output": "Rejects the task-count target and does not optimize task or phase cardinality; it uses the six evidenced ownership, dependency, validation, review, and repair seams when they bound expected total orchestration cost.", + "files": [] + }, + { + "id": "PD-02", + "prompt": "A plan allocates tests and a helper refactor but leaves the authoritative production path with no implementation owner.", + "expected_output": "Rejects helper-only allocation until every authoritative production path has a production owner and the production change, validation, and repair responsibility are explicitly allocated.", + "files": [] + }, + { + "id": "PD-03", + "prompt": "A planner groups two changes that have different owners, validation oracles, and independently routable repair outcomes.", + "expected_output": "Splits at the evidenced ownership, oracle, and repair frontier so a failure returns to the smallest affected plan region without widening unrelated accepted work.", + "files": [] + }, + { + "id": "PD-04", + "prompt": "A planner proposes splitting one production edit, its direct contract test, and its local documentation merely because three files are involved.", + "expected_output": "Keeps the coherent mechanical increment together under one production owner, oracle, and repair frontier; file count is not a decomposition seam.", + "files": [] + }, + { + "id": "PD-05", + "prompt": "A plan creates a new phase for each lifecycle label even though no dependency barrier or convergence boundary separates the work.", + "expected_output": "Rejects lifecycle-label phases and creates a phase only for an actual barrier or convergence boundary with concrete readiness and ownership evidence.", + "files": [] + }, + { + "id": "PD-06", + "prompt": "A specification is shortened by deleting a unique validation target and compatibility constraint while retaining repeated summary prose.", + "expected_output": "Restores a complete, nonredundant authority set: preserves every load-bearing field required downstream and removes duplicate prose rather than unique authority.", + "files": [] + }, + { + "id": "PD-07", + "prompt": "Execution proves one task materially under-decomposed after its repair frontier separates into two independently owned regions.", + "expected_output": "Stops repeatedly enlarging the task, requires a return to the plan, and reslices only the affected region while preserving the original binding, baseline, accepted unaffected regions, and typed repair route.", + "files": [] + }, + { + "id": "PD-09", + "prompt": "A planner proposes separate hardening, compatibility, and recovery tasks without current authority, repository, dependency, validation, or acceptance evidence for them.", + "expected_output": "Rejects speculative fragmentation and adds no tasks until a current material seam proves the scope; it does not create a second review, retry, or recovery subsystem.", + "files": [] + }, + { + "id": "PD-10", + "prompt": "One module exposes two entry points, but current repository evidence shows they share one production owner, oracle, and repair path.", + "expected_output": "Does not split by symbol count; independently owned entry points become separate tasks only when current repository evidence proves distinct production or repair seams.", + "files": [] } ], "v4_evals": [ diff --git a/rules/orchestration/orch-artifact-authoring.md b/rules/orchestration/orch-artifact-authoring.md index 4f4b835..6e1a5ca 100644 --- a/rules/orchestration/orch-artifact-authoring.md +++ b/rules/orchestration/orch-artifact-authoring.md @@ -24,7 +24,10 @@ Keep orchestration artifacts human-readable, contract-compliant, and executable - Reference stable spec IDs such as `REQ-`, `CON-`, `AC-`, `OQ-`, and `API-` in plans, phases, and tasks instead of repeating full requirement prose. - Provide concrete source files, target files, target symbols, validation instructions, and completion criteria in every task. - Carry execution context forward through spec-ID references plus file-level instructions only. -- Keep generated plans compact through minimum orchestration overhead while preserving Truth Basis continuity, independently falsifiable and testable increments, short evidence loops, complete source-spec coverage, explicit dependencies, disjoint write scopes, validation ownership, bounded failure radius, handoff requirements, and review boundaries. Do not split one mechanical increment merely to reduce task size or file count. +- Do not optimize task or phase cardinality. Bound expected total orchestration cost by decomposing only at concrete independently owned production, dependency, validation, review, and repair seams while preserving Truth Basis continuity, independently falsifiable increments, short evidence loops, and complete source-spec coverage. +- Assign every authoritative production path to a production owner. Reject helper-only allocation while a production path is unowned, and keep a coherent mechanical increment with one owner, oracle, and repair frontier together. +- Create phases only for an actual barrier or convergence boundary. Reject speculative splits unsupported by current authority, repository, dependency, ownership, validation, or acceptance evidence. +- When execution proves a task materially under-decomposed, return to the plan and reslice only the affected region while preserving the original binding, baseline, and accepted unaffected regions; do not repeatedly enlarge the task. - When plans contain contract-decoupled parallel tasks, include common contract groups, barrier participant maps, readiness criteria, release conditions, convergence owners, and task-level forbidden peer validation instructions. - Keep source-context, extra-evidence-loop, open-question, Knowledge Base Update, and body-level `Quality gate: verified|blocked` sections in specifications when required by the specification contract. - Summarize spec intent at most once in a root plan, then cite IDs for downstream detail. @@ -48,7 +51,7 @@ Contract loading by artifact type: - Repeat full requirement prose in plans, phases, or tasks when a spec-ID reference suffices. - Omit source files, target files, target symbols, validation rules, or completion criteria from executable tasks. - Use broad globs such as `src/**` as the only source or target path without exact files or narrow symbol-level explanation. -- Split phases or tasks solely because of template habit, lifecycle labels, or duplicated prose when a smaller artifact remains complete and executable. +- Split phases or tasks solely because of template habit, lifecycle labels, duplicated prose, a task-count target, or another cardinality preference when the coherent artifact remains complete and executable. - Encode sibling in-progress implementation files as dependencies for contract-decoupled parallel task validation; use common contracts, accepted prior handoffs, and post-barrier convergence instead. - Create phases or tasks whose target files are `.work-bundle/knowledge/**`. - Embed implementation plan tasks inside specifications. @@ -58,7 +61,7 @@ Contract loading by artifact type: - Confirm only the required contract files for the active artifact type were loaded. - Confirm plans, phases, and tasks cite relevant spec IDs and include concrete file-level execution instructions. -- Confirm compactness is explicitly checked, and any phase/task split is justified by distinct write scope, dependency, validation ownership, risk, barrier participation, or convergence ownership. +- Confirm decomposition is explicitly checked, every authoritative production path has a production owner, and any phase/task split is justified by a current dependency, ownership, validation, review, repair-frontier, barrier, or convergence seam. - Confirm contract groups, barrier metadata, convergence tasks, and forbidden peer validation appear where parallel tasks share a common contract. - Confirm no phase or task repeats more than a short one-line requirement summary without a spec-ID reference. - Confirm task files are self-contained for execution from the related spec plus their own instructions. diff --git a/skills/orch-create-implementation-plan/SKILL.md b/skills/orch-create-implementation-plan/SKILL.md index 6c05a55..aae1eb2 100644 --- a/skills/orch-create-implementation-plan/SKILL.md +++ b/skills/orch-create-implementation-plan/SKILL.md @@ -12,12 +12,13 @@ Plan only from a verified active specification with converged semantics, resolve ## Planning workflow 1. Use the specification and bounded repository evidence. Add upstream/downstream or validation scope only when current evidence proves it. -2. Use the minimum orchestration overhead that preserves Truth Basis continuity, independently falsifiable and testable increments, short evidence loops, exact dependencies, disjoint write scopes, validation ownership, bounded failure radius, and review boundaries. Do not split one mechanical increment when it already satisfies those constraints. +2. Do not optimize task or phase cardinality. Decompose only at concrete independently owned production, dependency, validation, review, and repair seams so expected total orchestration cost remains bounded while preserving independently falsifiable increments, short evidence loops, exact dependencies, disjoint write scopes, bounded failure radius, and review boundaries. Assign every authoritative production path to a production owner; reject helper-only allocation while its production path is unowned. Split independently owned entry points only when current repository evidence proves distinct ownership or repair seams. Keep one coherent mechanical increment with one owner, oracle, and repair frontier together. Do not create speculative splits unsupported by current authority, repository, dependency, ownership, validation, or acceptance evidence. 3. Give every task exact source IDs, a five-field Truth Basis, scope, interfaces, dependencies, steps, evidence, methodology, allocated rules/skills, executor profile, and review requirement. Allocate every accepted validation-bearing obligation to a stable `evidence_capability` invariant and the lightest capable task-local oracle. Each entry records source IDs, boundary, oracle, capability reason, freshness, task owner, validation evidence IDs, and initializes `closure_result: pending`. Use `no_validation_bearing_obligation + reason` only when no accepted validation-bearing obligation or design decision exists; never infer it from WOR-61 `none_relevant`. 4. Carry execution-workspace isolation, hydration, and cleanup policy into task and executor context; mutating siblings on the same execution path isolate via prepare_worktree or serialize even when write scopes are disjoint. -5. Use a common contract group before safe parallel work. Contract-decoupled participants depend on the common contract group and accepted prior handoffs, not sibling in-progress implementation output. Create explicit barrier metadata with barrier ID, readiness evidence, and convergence owner. Cross-branch or joint validation belongs to a post-barrier convergence task. +5. Use a common contract group before safe parallel work. Contract-decoupled participants depend on the common contract group and accepted prior handoffs, not sibling in-progress implementation output. Create a phase only for an actual barrier or convergence boundary, with explicit barrier ID, readiness evidence, and convergence owner. Cross-branch or joint validation belongs to a post-barrier convergence task. 6. Require a compact `executor-result-v1` handoff. Default `acceptance_review.required: false`. Require task review only when the task sets `acceptance_review.required: true`. Do not infer that flag from soft applicability prose. 7. When a consequential simplification or compatibility assumption exists, make the earliest ordinary task cheaply falsify it before broad edits. Do not add a risk score, checkpoint phase, or parallel lifecycle. +8. When execution proves a task materially under-decomposed, return to the plan and reslice only the affected region around the newly evidenced seam. Preserve the original binding, baseline, and accepted unaffected regions; do not repeatedly enlarge the task. ## Methodology allocation diff --git a/skills/orch-create-specification/SKILL.md b/skills/orch-create-specification/SKILL.md index af33606..714b4bf 100644 --- a/skills/orch-create-specification/SKILL.md +++ b/skills/orch-create-specification/SKILL.md @@ -7,7 +7,7 @@ description: 'Create or repair an AI-ready WorkBundle implementation specificati ## Scope -Create the smallest authoritative specification under `.work-bundle/orchestration/spec/active/`. Do not implement source changes or provision execution workspaces. +Create a complete, nonredundant authoritative specification under `.work-bundle/orchestration/spec/active/`. Preserve every load-bearing requirement, constraint, interface, acceptance criterion, validation target, and decision needed downstream; such authority must not be removed merely to make the artifact smaller. Reject duplicate prose that adds no authority. Do not implement source changes or provision execution workspaces. ## Workflow diff --git a/tests/test_orchestration_skill_rule_boundary.py b/tests/test_orchestration_skill_rule_boundary.py index bda6d3c..dc802fa 100644 --- a/tests/test_orchestration_skill_rule_boundary.py +++ b/tests/test_orchestration_skill_rule_boundary.py @@ -81,10 +81,10 @@ def test_specification_uses_compact_semantic_convergence_and_workspace_policy() def test_planner_allocates_methodology_capability_and_bounded_context() -> None: text = read("skills/orch-create-implementation-plan/SKILL.md") for token in [ - "minimum orchestration overhead", + "expected total orchestration cost", "independently falsifiable", "bounded failure radius", - "Do not split one mechanical increment", + "coherent mechanical increment", "source-ID coverage", "dev-systematic-debugging", "dev-test-driven-development", diff --git a/tests/test_orchestration_workflow_contracts.py b/tests/test_orchestration_workflow_contracts.py index b4db039..76bb558 100644 --- a/tests/test_orchestration_workflow_contracts.py +++ b/tests/test_orchestration_workflow_contracts.py @@ -944,7 +944,7 @@ def test_workflow_separates_durable_artifacts_from_runtime_packets() -> None: "earliest ordinary task", "knowledge disposition", "review owns approved persistence", - "minimum orchestration overhead", + "expected total orchestration cost", "accepted task dispositions", ]: assert token in workflow diff --git a/tests/test_wor109_planner_contracts.py b/tests/test_wor109_planner_contracts.py new file mode 100644 index 0000000..f456615 --- /dev/null +++ b/tests/test_wor109_planner_contracts.py @@ -0,0 +1,68 @@ +import json +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +def read(path: str) -> str: + return (ROOT / path).read_text(encoding="utf-8") + + +def test_heavy_planning_decomposes_from_production_and_repair_seams() -> None: + planner = read("skills/orch-create-implementation-plan/SKILL.md") + rule = read("rules/orchestration/orch-artifact-authoring.md") + plan = read("references/assets/orchestration/contract/plan-v1.md") + workflow = read("references/assets/orchestration/workflow.md") + + for text in (planner, rule, plan, workflow): + assert "minimum orchestration overhead" not in text + assert "task or phase cardinality" in text + assert "expected total orchestration cost" in text + assert "authoritative production path" in text + assert "production owner" in text + assert "repair frontier" in text + assert "speculative" in text + assert "materially under-decomposed" in text + assert "reslice only the affected region" in text + assert "repeatedly enlarge" in text + + for text in (planner, plan, workflow): + assert "actual barrier or convergence" in text + assert "coherent mechanical increment" in text + + assert "helper-only" in planner + assert "independently owned entry points" in planner + assert "current repository evidence" in planner + + +def test_specification_contract_requires_complete_nonredundant_authority() -> None: + specification = read("skills/orch-create-specification/SKILL.md") + + assert "smallest authoritative specification" not in specification + assert "complete, nonredundant authoritative specification" in specification + assert "load-bearing" in specification + assert "duplicate prose" in specification + assert "must not be removed merely to make the artifact smaller" in specification + + +def test_pd_pressure_rows_cover_review_stable_decomposition() -> None: + payload = json.loads(read("references/evals/orchestration/evals.json")) + cases = {str(case["id"]): case for case in payload["evals"]} + expected_ids = {f"PD-{number:02d}" for number in (*range(1, 8), 9, 10)} + + assert expected_ids <= cases.keys() + + combined = { + case_id: f"{cases[case_id]['prompt']} {cases[case_id]['expected_output']}" + for case_id in expected_ids + } + assert "task-count target" in combined["PD-01"] + assert "authoritative production path" in combined["PD-02"] + assert "repair frontier" in combined["PD-03"] + assert "coherent mechanical increment" in combined["PD-04"] + assert "actual barrier" in combined["PD-05"] + assert "complete, nonredundant" in combined["PD-06"] + assert "return to the plan" in combined["PD-07"] + assert "speculative" in combined["PD-09"] + assert "independently owned entry points" in combined["PD-10"] From f55640a2bfb4afcce032cbd8d1dc6a6eacc1214f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 07:29:52 +0800 Subject: [PATCH 06/48] feat(orchestration): bound planner reslice runtime --- .../contract/stage-event-v1.schema.json | 29 ++- scripts/orchestration/review_runtime.py | 113 ++++++++++- scripts/work-bundle/stage_events.py | 59 +++++- tests/test_orchestration_reviews.py | 11 ++ tests/test_wor109_planner_runtime.py | 187 ++++++++++++++++++ 5 files changed, 389 insertions(+), 10 deletions(-) create mode 100644 tests/test_wor109_planner_runtime.py diff --git a/references/assets/orchestration/contract/stage-event-v1.schema.json b/references/assets/orchestration/contract/stage-event-v1.schema.json index 3e0db00..0006b9d 100644 --- a/references/assets/orchestration/contract/stage-event-v1.schema.json +++ b/references/assets/orchestration/contract/stage-event-v1.schema.json @@ -55,6 +55,32 @@ "mutation_epoch": {"type": ["integer", "null"], "minimum": 0} } }, + "planningEconomics": { + "type": "object", + "additionalProperties": false, + "required": [ + "initial_cardinality", "plan_revisions", "plan_reviews", + "scope_allocation_repairs", "task_review_repairs", "validation_reruns", + "first_green_to_final_accept_ms" + ], + "properties": { + "initial_cardinality": { + "type": "object", + "additionalProperties": false, + "required": ["phases", "tasks"], + "properties": { + "phases": {"type": "integer", "minimum": 0}, + "tasks": {"type": "integer", "minimum": 0} + } + }, + "plan_revisions": {"type": "integer", "minimum": 0}, + "plan_reviews": {"type": "integer", "minimum": 0}, + "scope_allocation_repairs": {"type": "integer", "minimum": 0}, + "task_review_repairs": {"type": "integer", "minimum": 0}, + "validation_reruns": {"type": "integer", "minimum": 0}, + "first_green_to_final_accept_ms": {"type": ["integer", "null"], "minimum": 0} + } + }, "stageEvent": { "type": "object", "additionalProperties": false, @@ -77,7 +103,8 @@ "return_reason": {"anyOf": [{"type": "string", "minLength": 1}, {"type": "null"}]}, "owner": {"anyOf": [{"type": "string", "minLength": 1}, {"type": "null"}]}, "identity": {"$ref": "#/$defs/identity"}, - "privacy": {"const": "operational_metadata_only"} + "privacy": {"const": "operational_metadata_only"}, + "planning_economics": {"$ref": "#/$defs/planningEconomics"} } } } diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index 9113720..13850db 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -61,6 +61,23 @@ } ) TARGET_KEYS = frozenset({"artifact_id", "revision", "sha256", "source_tree"}) +PLAN_RETURN_KEYS = frozenset( + { + "finding_id", + "first_broken_artifact", + "return_to", + "action", + "execution_state", + "affected_region", + "returned_authority_identity", + "preserved_evidence_identities", + "resume_requires", + "preserve_original_binding", + "preserve_original_baseline", + "preserve_valid_work_and_evidence", + "silent_expansion_allowed", + } +) EVIDENCE_ITEM_KEYS = frozenset({"kind", "locator", "digest_or_identity", "observation"}) STAGE_REVIEW_KEYS = frozenset( {"review_id", "review_mode", "review_target_kind", "repair_frontier", "review_reset", "stage", "target_identity", "reviewer", "evidence", "verdict", "findings", "started_at", "completed_at", "staleness"} @@ -723,7 +740,9 @@ def validate_review_finding(value: Mapping[str, Any]) -> ReviewFindingV1: def route_review_verdict( - value: Mapping[str, Any], *, previous_scope_expansions: int = 0 + value: Mapping[str, Any], *, previous_scope_expansions: int = 0, + affected_region: Sequence[str] | None = None, + unaffected_evidence_identities: Sequence[Mapping[str, Any]] = (), ) -> dict[str, Any]: finding = validate_review_finding(value) expected_action = ROUTES[finding.finding_class][2] @@ -731,16 +750,102 @@ def route_review_verdict( raise ReviewContractError("terminal adjudicator disposition cannot authorize repair mutation") if finding.disposition != expected_action: raise ReviewContractError("review finding routing disposition is invalid") - repeated_reslice = expected_action == "reslice_plan" and previous_scope_expansions > 0 - return { + if not isinstance(previous_scope_expansions, int) or isinstance(previous_scope_expansions, bool) or previous_scope_expansions < 0: + raise ReviewContractError("previous_scope_expansions must be a non-negative integer") + result = { "finding_id": finding.finding_id, "first_broken_artifact": finding.first_broken_artifact, "return_to": finding.recommended_owner, "action": expected_action, - "execution_state": "paused_for_reslice" if repeated_reslice else "returned_for_repair", + "execution_state": "paused_for_reslice" if expected_action == "reslice_plan" else "returned_for_repair", "preserve_valid_work_and_evidence": True, "silent_expansion_allowed": False, } + if expected_action != "reslice_plan": + if affected_region is not None or unaffected_evidence_identities: + raise ReviewContractError("affected region applies only to a plan reslice") + return result + + region = list(affected_region) if affected_region is not None else [finding.target_identity["artifact_id"]] + if ( + not region + or any(not isinstance(item, str) or not ID_RE.fullmatch(item) for item in region) + or len(region) != len(set(region)) + ): + raise ReviewContractError("affected region must contain unique valid artifact ids") + preserved = [dict(_target_identity(item, "unaffected_evidence_identity")) for item in unaffected_evidence_identities] + preserved_ids = [item["artifact_id"] for item in preserved] + if len(preserved_ids) != len(set(preserved_ids)) or set(region).intersection(preserved_ids): + raise ReviewContractError("affected region and unaffected evidence must be disjoint and unambiguous") + result.update( + { + "affected_region": region, + "returned_authority_identity": dict(finding.target_identity), + "preserved_evidence_identities": preserved, + "resume_requires": "accepted_repaired_plan_authority", + "preserve_original_binding": True, + "preserve_original_baseline": True, + } + ) + return result + + +def resume_plan_return( + value: Mapping[str, Any], *, + accepted_repaired_authority_identity: Mapping[str, Any], + current_unaffected_evidence_identities: Sequence[Mapping[str, Any]], +) -> dict[str, Any]: + """Admit a paused affected region only from repaired authority and unchanged evidence.""" + + record = _mapping(value, "plan_return") + _closed(record, PLAN_RETURN_KEYS, "plan_return") + _identifier(record["finding_id"], "plan_return.finding_id") + if ( + record["first_broken_artifact"] != "plan" + or record["return_to"] != "plan_owner" + or record["action"] != "reslice_plan" + or record["execution_state"] != "paused_for_reslice" + or record["resume_requires"] != "accepted_repaired_plan_authority" + or record["preserve_original_binding"] is not True + or record["preserve_original_baseline"] is not True + or record["preserve_valid_work_and_evidence"] is not True + or record["silent_expansion_allowed"] is not False + ): + raise ReviewContractError("plan return is not a paused bounded reslice") + region = record["affected_region"] + if ( + not isinstance(region, list) + or not region + or any(not isinstance(item, str) or not ID_RE.fullmatch(item) for item in region) + or len(region) != len(set(region)) + ): + raise ReviewContractError("affected region must contain unique valid artifact ids") + returned = dict( + _target_identity(record["returned_authority_identity"], "returned_authority_identity") + ) + repaired = dict( + _target_identity(accepted_repaired_authority_identity, "accepted_repaired_authority_identity") + ) + if repaired["artifact_id"] != returned["artifact_id"] or repaired == returned: + raise ReviewContractError("resume requires new accepted repaired authority") + + preserved = [ + dict(_target_identity(item, "preserved_evidence_identity")) + for item in record["preserved_evidence_identities"] + ] + preserved_ids = [item["artifact_id"] for item in preserved] + if len(preserved_ids) != len(set(preserved_ids)) or set(region).intersection(preserved_ids): + raise ReviewContractError("affected region and unaffected evidence must be disjoint and unambiguous") + current = [ + dict(_target_identity(item, "current_unaffected_evidence_identity")) + for item in current_unaffected_evidence_identities + ] + if current != preserved: + raise ReviewContractError("unaffected evidence identities changed during bounded reslice") + resumed = dict(record) + resumed["execution_state"] = "ready_from_repaired_authority" + resumed["returned_authority_identity"] = repaired + return resumed def transition_review_finding( diff --git a/scripts/work-bundle/stage_events.py b/scripts/work-bundle/stage_events.py index 430a9b6..fe5b7a7 100644 --- a/scripts/work-bundle/stage_events.py +++ b/scripts/work-bundle/stage_events.py @@ -61,6 +61,18 @@ "expansion_reason", } ) +PLANNING_ECONOMICS_FIELDS = frozenset( + { + "initial_cardinality", + "plan_revisions", + "plan_reviews", + "scope_allocation_repairs", + "task_review_repairs", + "validation_reruns", + "first_green_to_final_accept_ms", + } +) +INITIAL_CARDINALITY_FIELDS = frozenset({"phases", "tasks"}) CONTEXT_EXPANSION_REASONS = frozenset( {None, "failed_validation", "ambiguity", "reviewer_request", "authority_gap"} ) @@ -82,7 +94,8 @@ "privacy", } ) -EVENT_FIELDS = LEGACY_EVENT_FIELDS | {"compiled_context_metrics"} +OPTIONAL_EVENT_FIELDS = frozenset({"compiled_context_metrics", "planning_economics"}) +EVENT_FIELDS = LEGACY_EVENT_FIELDS | OPTIONAL_EVENT_FIELDS _ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:-]*$") _OID_RE = re.compile(r"^[0-9a-f]{40}$") @@ -121,11 +134,14 @@ class StageEventV1: identity: dict[str, str | int | None] privacy: str compiled_context_metrics: dict[str, int | str | None] | None = None + planning_economics: dict[str, int | dict[str, int] | None] | None = None def to_dict(self) -> dict[str, object]: result = asdict(self) if self.compiled_context_metrics is None: result.pop("compiled_context_metrics") + if self.planning_economics is None: + result.pop("planning_economics") return result @@ -178,10 +194,12 @@ def validate_stage_event(payload: Mapping[str, object]) -> StageEventV1: """Validate one closed API-004 event without retaining caller-owned containers.""" _scan_for_sensitive_content(payload) - if set(payload) == LEGACY_EVENT_FIELDS: - value = payload - else: - value = _validate_exact_fields(payload, EVENT_FIELDS, "WB_STAGE_EVENT_FIELDS_INVALID") + if not isinstance(payload, Mapping): + _fail("WB_STAGE_EVENT_FIELDS_INVALID") + fields = set(payload) + if not LEGACY_EVENT_FIELDS.issubset(fields) or not fields.issubset(EVENT_FIELDS): + _fail("WB_STAGE_EVENT_FIELDS_INVALID") + value = payload for field in ("event_id", "process_id", "attempt_id"): if not _is_id(value[field]): _fail("WB_STAGE_EVENT_ID_INVALID") @@ -245,6 +263,36 @@ def validate_stage_event(payload: Mapping[str, object]) -> StageEventV1: _fail("WB_STAGE_EVENT_CONTEXT_METRICS_INVALID") metrics = {field: checked[field] for field in sorted(COMPILED_CONTEXT_METRIC_FIELDS)} + economics_value = value.get("planning_economics") + economics: dict[str, int | dict[str, int] | None] | None = None + if economics_value is not None: + checked = _validate_exact_fields( + economics_value, + PLANNING_ECONOMICS_FIELDS, + "WB_STAGE_EVENT_ECONOMICS_INVALID", + ) + cardinality = _validate_exact_fields( + checked["initial_cardinality"], + INITIAL_CARDINALITY_FIELDS, + "WB_STAGE_EVENT_ECONOMICS_INVALID", + ) + if any(not _is_nonnegative_int(cardinality[field]) for field in INITIAL_CARDINALITY_FIELDS): + _fail("WB_STAGE_EVENT_ECONOMICS_INVALID") + count_fields = PLANNING_ECONOMICS_FIELDS - { + "initial_cardinality", + "first_green_to_final_accept_ms", + } + if any(not _is_nonnegative_int(checked[field]) for field in count_fields): + _fail("WB_STAGE_EVENT_ECONOMICS_INVALID") + latency = checked["first_green_to_final_accept_ms"] + if latency is not None and not _is_nonnegative_int(latency): + _fail("WB_STAGE_EVENT_ECONOMICS_INVALID") + economics = { + "initial_cardinality": {field: cardinality[field] for field in sorted(INITIAL_CARDINALITY_FIELDS)}, + **{field: checked[field] for field in sorted(count_fields)}, + "first_green_to_final_accept_ms": latency, + } + return StageEventV1( event_id=str(value["event_id"]), timestamp=str(value["timestamp"]), @@ -261,6 +309,7 @@ def validate_stage_event(payload: Mapping[str, object]) -> StageEventV1: identity={field: identity[field] for field in sorted(IDENTITY_FIELDS)}, privacy="operational_metadata_only", compiled_context_metrics=metrics, + planning_economics=economics, ) diff --git a/tests/test_orchestration_reviews.py b/tests/test_orchestration_reviews.py index 6727d1d..120530e 100644 --- a/tests/test_orchestration_reviews.py +++ b/tests/test_orchestration_reviews.py @@ -179,6 +179,17 @@ def test_api_001_reslice_pauses_repeated_expansion_and_preserves_evidence() -> N "return_to": "plan_owner", "action": "reslice_plan", "execution_state": "paused_for_reslice", + "affected_region": ["task-b01"], + "returned_authority_identity": { + "artifact_id": "task-b01", + "revision": "1", + "sha256": ZERO_SHA, + "source_tree": ZERO_TREE, + }, + "preserved_evidence_identities": [], + "resume_requires": "accepted_repaired_plan_authority", + "preserve_original_binding": True, + "preserve_original_baseline": True, "preserve_valid_work_and_evidence": True, "silent_expansion_allowed": False, } diff --git a/tests/test_wor109_planner_runtime.py b/tests/test_wor109_planner_runtime.py new file mode 100644 index 0000000..68dd308 --- /dev/null +++ b/tests/test_wor109_planner_runtime.py @@ -0,0 +1,187 @@ +from __future__ import annotations + +import json +import sys +from copy import deepcopy +from pathlib import Path + +import pytest + + +REPO_ROOT = Path(__file__).resolve().parents[1] +ORCHESTRATION = REPO_ROOT / "scripts" / "orchestration" +WORK_BUNDLE = REPO_ROOT / "scripts" / "work-bundle" +for path in (ORCHESTRATION, WORK_BUNDLE): + if str(path) not in sys.path: + sys.path.insert(0, str(path)) + +from review_runtime import ( # noqa: E402 + ReviewContractError, + classify_first_broken_owner, + resume_plan_return, + route_review_verdict, +) +from stage_events import StageEventError, validate_stage_event # noqa: E402 + + +ZERO_SHA = "0" * 64 +ZERO_TREE = "0" * 40 + + +def identity(artifact_id: str, revision: str = "1") -> dict[str, object]: + return { + "artifact_id": artifact_id, + "revision": revision, + "sha256": ZERO_SHA, + "source_tree": ZERO_TREE, + } + + +def allocation_gap() -> dict[str, object]: + artifact, owner, disposition = classify_first_broken_owner("allocation_gap") + return { + "finding_id": "finding-under-decomposed", + "stage": "implementation", + "class": "allocation_gap", + "severity": "blocking", + "first_broken_artifact": artifact, + "obligation_basis": "accepted_requirement", + "evidence": [ + { + "kind": "runtime", + "locator": "task-004", + "digest_or_identity": "repair-frontier-separated", + "observation": "The task now has two independently owned repair regions.", + } + ], + "target_identity": identity("plan-001"), + "summary": "The affected task is materially under-decomposed.", + "recommended_owner": owner, + "disposition": disposition, + } + + +def event() -> dict[str, object]: + return { + "event_id": "event-economics", + "timestamp": "2026-09-07T00:00:00Z", + "process_id": "process-001", + "stage": "integrated_implementation", + "attempt_id": "attempt-001", + "event_type": "stage_completed", + "enforcement_mode": "native", + "join_ids": { + "specification_id": "spec-001", + "plan_id": "plan-001", + "phase_id": "phase-001", + "task_id": None, + "review_id": "review-001", + "evaluation_id": None, + }, + "clocks": {"wall_ms": 13, "active_ms": 8, "billed_ms": None}, + "finding_class": None, + "return_reason": None, + "owner": "plan_owner", + "identity": { + "product_tree": ZERO_TREE, + "artifact_digest": ZERO_SHA, + "mutation_epoch": 2, + }, + "privacy": "operational_metadata_only", + "planning_economics": { + "initial_cardinality": {"phases": 1, "tasks": 6}, + "plan_revisions": 2, + "plan_reviews": 3, + "scope_allocation_repairs": 1, + "task_review_repairs": 2, + "validation_reruns": 4, + "first_green_to_final_accept_ms": 1200, + }, + } + + +def test_pd_07_returns_only_affected_region_and_preserves_unaffected_evidence() -> None: + preserved = [identity("task-001"), identity("task-002")] + + routed = route_review_verdict( + allocation_gap(), + affected_region=["task-003"], + unaffected_evidence_identities=preserved, + ) + + assert routed["execution_state"] == "paused_for_reslice" + assert routed["affected_region"] == ["task-003"] + assert routed["returned_authority_identity"] == identity("plan-001") + assert routed["preserved_evidence_identities"] == preserved + assert routed["resume_requires"] == "accepted_repaired_plan_authority" + assert routed["preserve_original_binding"] is True + assert routed["preserve_original_baseline"] is True + assert routed["silent_expansion_allowed"] is False + + +def test_pd_07_resume_waits_for_new_authority_and_exact_preserved_evidence() -> None: + preserved = [identity("task-001")] + routed = route_review_verdict( + allocation_gap(), + affected_region=["task-003"], + unaffected_evidence_identities=preserved, + ) + + with pytest.raises(ReviewContractError, match="repaired authority"): + resume_plan_return( + routed, + accepted_repaired_authority_identity=identity("plan-001"), + current_unaffected_evidence_identities=preserved, + ) + + changed = deepcopy(preserved) + changed[0]["revision"] = "2" + with pytest.raises(ReviewContractError, match="unaffected evidence"): + resume_plan_return( + routed, + accepted_repaired_authority_identity=identity("plan-001", "2"), + current_unaffected_evidence_identities=changed, + ) + + resumed = resume_plan_return( + routed, + accepted_repaired_authority_identity=identity("plan-001", "2"), + current_unaffected_evidence_identities=preserved, + ) + assert resumed["execution_state"] == "ready_from_repaired_authority" + assert resumed["preserved_evidence_identities"] == preserved + + +@pytest.mark.parametrize("affected_region", [[], ["task-003", "task-003"], [""]]) +def test_pd_07_rejects_unbounded_or_ambiguous_affected_regions(affected_region) -> None: + with pytest.raises(ReviewContractError, match="affected region"): + route_review_verdict(allocation_gap(), affected_region=affected_region) + + +def test_pd_08_emits_closed_nonjudgmental_planning_economics() -> None: + value = event() + validated = validate_stage_event(value) + + assert validated.to_dict()["planning_economics"] == value["planning_economics"] + + # Cardinality is observed, not judged: large and zero values remain valid metadata. + value["planning_economics"]["initial_cardinality"] = {"phases": 0, "tasks": 100_000} + assert validate_stage_event(value).planning_economics["initial_cardinality"]["tasks"] == 100_000 + + schema = json.loads( + (REPO_ROOT / "references/assets/orchestration/contract/stage-event-v1.schema.json").read_text() + ) + assert schema["$defs"]["stageEvent"]["properties"]["planning_economics"] == { + "$ref": "#/$defs/planningEconomics" + } + assert schema["$defs"]["planningEconomics"]["additionalProperties"] is False + + +def test_pd_08_economics_is_closed_and_allows_pending_accept_latency() -> None: + value = event() + value["planning_economics"]["first_green_to_final_accept_ms"] = None + assert validate_stage_event(value).planning_economics["first_green_to_final_accept_ms"] is None + + value["planning_economics"]["task_count_verdict"] = "too-many" + with pytest.raises(StageEventError, match="WB_STAGE_EVENT_ECONOMICS_INVALID"): + validate_stage_event(value) From e4a6c3b0bb654f94a8836bdbc2bbb8c4735238d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 07:40:27 +0800 Subject: [PATCH 07/48] fix(orchestration): bind planner reslice authority --- scripts/orchestration/review_runtime.py | 126 +++++++++--- scripts/work-bundle/stage_events.py | 132 ++++++++++++- tests/test_orchestration_reviews.py | 38 +++- tests/test_wor109_planner_runtime.py | 253 ++++++++++++++++++++---- 4 files changed, 467 insertions(+), 82 deletions(-) diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index 13850db..b6da5c7 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -61,6 +61,9 @@ } ) TARGET_KEYS = frozenset({"artifact_id", "revision", "sha256", "source_tree"}) +AFFECTED_REGION_KEYS = frozenset({"task_ids", "paths", "interfaces", "validation_oracles"}) +BINDING_IDENTITY_KEYS = frozenset({"binding_id", "sha256"}) +BASELINE_IDENTITY_KEYS = frozenset({"head", "tree"}) PLAN_RETURN_KEYS = frozenset( { "finding_id", @@ -72,8 +75,8 @@ "returned_authority_identity", "preserved_evidence_identities", "resume_requires", - "preserve_original_binding", - "preserve_original_baseline", + "original_binding_identity", + "original_baseline_identity", "preserve_valid_work_and_evidence", "silent_expansion_allowed", } @@ -691,6 +694,49 @@ def classify_first_broken_owner(finding_class: str) -> tuple[str, str, str]: raise ReviewContractError(f"class is not classified: {finding_class}") from error +def _affected_region(value: Any) -> dict[str, list[str]]: + region = _mapping(value, "affected region") + _closed(region, AFFECTED_REGION_KEYS, "affected region") + result: dict[str, list[str]] = {} + for field in ("task_ids", "interfaces", "validation_oracles"): + items = _string_list(region[field], f"affected region.{field}") + if len(items) != len(set(items)) or any(not ID_RE.fullmatch(item) for item in items): + raise ReviewContractError(f"affected region.{field} must contain unique valid ids") + result[field] = list(items) + if not result["task_ids"]: + raise ReviewContractError("affected region.task_ids must be non-empty") + paths = _string_list(region["paths"], "affected region.paths") + if len(paths) != len(set(paths)): + raise ReviewContractError("affected region.paths must be unique") + for item in paths: + path = Path(item) + if path.is_absolute() or any(part in {"", ".", ".."} for part in path.parts): + raise ReviewContractError("affected region.paths must be canonical relative paths") + result["paths"] = list(paths) + return {field: result[field] for field in ("task_ids", "paths", "interfaces", "validation_oracles")} + + +def _binding_identity(value: Any) -> dict[str, str]: + identity = _mapping(value, "binding identity") + _closed(identity, BINDING_IDENTITY_KEYS, "binding identity") + binding_id = _identifier(identity["binding_id"], "binding identity.binding_id") + digest = identity["sha256"] + if not isinstance(digest, str) or not SHA256_RE.fullmatch(digest): + raise ReviewContractError("binding identity.sha256 must be a lowercase SHA-256") + return {"binding_id": binding_id, "sha256": digest} + + +def _baseline_identity(value: Any) -> dict[str, str]: + identity = _mapping(value, "baseline identity") + _closed(identity, BASELINE_IDENTITY_KEYS, "baseline identity") + if any( + not isinstance(identity[field], str) or not GIT_OID_RE.fullmatch(identity[field]) + for field in BASELINE_IDENTITY_KEYS + ): + raise ReviewContractError("baseline identity must contain exact Git head and tree ids") + return {"head": str(identity["head"]), "tree": str(identity["tree"])} + + def validate_review_finding(value: Mapping[str, Any]) -> ReviewFindingV1: record = _mapping(value, "review_finding_v1") _closed(record, FINDING_KEYS, "review_finding_v1") @@ -741,8 +787,10 @@ def validate_review_finding(value: Mapping[str, Any]) -> ReviewFindingV1: def route_review_verdict( value: Mapping[str, Any], *, previous_scope_expansions: int = 0, - affected_region: Sequence[str] | None = None, + affected_region: Mapping[str, Any] | None = None, unaffected_evidence_identities: Sequence[Mapping[str, Any]] = (), + original_binding_identity: Mapping[str, Any] | None = None, + original_baseline_identity: Mapping[str, Any] | None = None, ) -> dict[str, Any]: finding = validate_review_finding(value) expected_action = ROUTES[finding.finding_class][2] @@ -750,32 +798,42 @@ def route_review_verdict( raise ReviewContractError("terminal adjudicator disposition cannot authorize repair mutation") if finding.disposition != expected_action: raise ReviewContractError("review finding routing disposition is invalid") - if not isinstance(previous_scope_expansions, int) or isinstance(previous_scope_expansions, bool) or previous_scope_expansions < 0: + if ( + not isinstance(previous_scope_expansions, int) + or isinstance(previous_scope_expansions, bool) + or previous_scope_expansions < 0 + ): raise ReviewContractError("previous_scope_expansions must be a non-negative integer") result = { "finding_id": finding.finding_id, "first_broken_artifact": finding.first_broken_artifact, "return_to": finding.recommended_owner, "action": expected_action, - "execution_state": "paused_for_reslice" if expected_action == "reslice_plan" else "returned_for_repair", + "execution_state": ( + "paused_for_reslice" if expected_action == "reslice_plan" else "returned_for_repair" + ), "preserve_valid_work_and_evidence": True, "silent_expansion_allowed": False, } if expected_action != "reslice_plan": - if affected_region is not None or unaffected_evidence_identities: + if ( + affected_region is not None + or unaffected_evidence_identities + or original_binding_identity is not None + or original_baseline_identity is not None + ): raise ReviewContractError("affected region applies only to a plan reslice") return result - region = list(affected_region) if affected_region is not None else [finding.target_identity["artifact_id"]] - if ( - not region - or any(not isinstance(item, str) or not ID_RE.fullmatch(item) for item in region) - or len(region) != len(set(region)) - ): - raise ReviewContractError("affected region must contain unique valid artifact ids") - preserved = [dict(_target_identity(item, "unaffected_evidence_identity")) for item in unaffected_evidence_identities] + region = _affected_region(affected_region) + binding = _binding_identity(original_binding_identity) + baseline = _baseline_identity(original_baseline_identity) + preserved = [ + dict(_target_identity(item, "unaffected_evidence_identity")) + for item in unaffected_evidence_identities + ] preserved_ids = [item["artifact_id"] for item in preserved] - if len(preserved_ids) != len(set(preserved_ids)) or set(region).intersection(preserved_ids): + if len(preserved_ids) != len(set(preserved_ids)) or set(region["task_ids"]).intersection(preserved_ids): raise ReviewContractError("affected region and unaffected evidence must be disjoint and unambiguous") result.update( { @@ -783,8 +841,8 @@ def route_review_verdict( "returned_authority_identity": dict(finding.target_identity), "preserved_evidence_identities": preserved, "resume_requires": "accepted_repaired_plan_authority", - "preserve_original_binding": True, - "preserve_original_baseline": True, + "original_binding_identity": binding, + "original_baseline_identity": baseline, } ) return result @@ -792,7 +850,10 @@ def route_review_verdict( def resume_plan_return( value: Mapping[str, Any], *, - accepted_repaired_authority_identity: Mapping[str, Any], + workspace_root: Path, + plan_path: Path, + current_binding_identity: Mapping[str, Any], + current_baseline_identity: Mapping[str, Any], current_unaffected_evidence_identities: Sequence[Mapping[str, Any]], ) -> dict[str, Any]: """Admit a paused affected region only from repaired authority and unchanged evidence.""" @@ -806,26 +867,27 @@ def resume_plan_return( or record["action"] != "reslice_plan" or record["execution_state"] != "paused_for_reslice" or record["resume_requires"] != "accepted_repaired_plan_authority" - or record["preserve_original_binding"] is not True - or record["preserve_original_baseline"] is not True or record["preserve_valid_work_and_evidence"] is not True or record["silent_expansion_allowed"] is not False ): raise ReviewContractError("plan return is not a paused bounded reslice") - region = record["affected_region"] - if ( - not isinstance(region, list) - or not region - or any(not isinstance(item, str) or not ID_RE.fullmatch(item) for item in region) - or len(region) != len(set(region)) - ): - raise ReviewContractError("affected region must contain unique valid artifact ids") + region = _affected_region(record["affected_region"]) + original_binding = _binding_identity(record["original_binding_identity"]) + original_baseline = _baseline_identity(record["original_baseline_identity"]) + if _binding_identity(current_binding_identity) != original_binding: + raise ReviewContractError("original binding identity changed during bounded reslice") + if _baseline_identity(current_baseline_identity) != original_baseline: + raise ReviewContractError("original baseline identity changed during bounded reslice") returned = dict( _target_identity(record["returned_authority_identity"], "returned_authority_identity") ) - repaired = dict( - _target_identity(accepted_repaired_authority_identity, "accepted_repaired_authority_identity") - ) + try: + repaired = dict(plan_review_identity(Path(workspace_root), Path(plan_path))) + _require_current_review(Path(workspace_root), "plan", repaired) + except (OSError, SystemExit, ValueError) as error: + raise ReviewContractError( + "resume requires current accepted repaired plan-review authority" + ) from error if repaired["artifact_id"] != returned["artifact_id"] or repaired == returned: raise ReviewContractError("resume requires new accepted repaired authority") @@ -834,7 +896,7 @@ def resume_plan_return( for item in record["preserved_evidence_identities"] ] preserved_ids = [item["artifact_id"] for item in preserved] - if len(preserved_ids) != len(set(preserved_ids)) or set(region).intersection(preserved_ids): + if len(preserved_ids) != len(set(preserved_ids)) or set(region["task_ids"]).intersection(preserved_ids): raise ReviewContractError("affected region and unaffected evidence must be disjoint and unambiguous") current = [ dict(_target_identity(item, "current_unaffected_evidence_identity")) diff --git a/scripts/work-bundle/stage_events.py b/scripts/work-bundle/stage_events.py index fe5b7a7..1c8522a 100644 --- a/scripts/work-bundle/stage_events.py +++ b/scripts/work-bundle/stage_events.py @@ -11,7 +11,7 @@ from dataclasses import asdict, dataclass from datetime import datetime from pathlib import Path -from typing import Any, Mapping +from typing import Any, Mapping, Sequence EVENT_TYPES = frozenset( @@ -190,7 +190,9 @@ def _validate_exact_fields(value: object, expected: frozenset[str], code: str) - return value -def validate_stage_event(payload: Mapping[str, object]) -> StageEventV1: +def _validate_stage_event( + payload: Mapping[str, object], *, allow_derived_economics: bool +) -> StageEventV1: """Validate one closed API-004 event without retaining caller-owned containers.""" _scan_for_sensitive_content(payload) @@ -266,6 +268,8 @@ def validate_stage_event(payload: Mapping[str, object]) -> StageEventV1: economics_value = value.get("planning_economics") economics: dict[str, int | dict[str, int] | None] | None = None if economics_value is not None: + if not allow_derived_economics: + _fail("WB_STAGE_EVENT_ECONOMICS_INVALID") checked = _validate_exact_fields( economics_value, PLANNING_ECONOMICS_FIELDS, @@ -288,7 +292,9 @@ def validate_stage_event(payload: Mapping[str, object]) -> StageEventV1: if latency is not None and not _is_nonnegative_int(latency): _fail("WB_STAGE_EVENT_ECONOMICS_INVALID") economics = { - "initial_cardinality": {field: cardinality[field] for field in sorted(INITIAL_CARDINALITY_FIELDS)}, + "initial_cardinality": { + field: cardinality[field] for field in sorted(INITIAL_CARDINALITY_FIELDS) + }, **{field: checked[field] for field in sorted(count_fields)}, "first_green_to_final_accept_ms": latency, } @@ -313,6 +319,115 @@ def validate_stage_event(payload: Mapping[str, object]) -> StageEventV1: ) +def validate_stage_event(payload: Mapping[str, object]) -> StageEventV1: + """Validate one event record, including a stored derived economics projection.""" + + return _validate_stage_event(payload, allow_derived_economics=True) + + +def derive_planning_economics( + records: Sequence[StageEventV1], +) -> dict[str, int | dict[str, int] | None]: + """Derive neutral planning-cost observations from the typed event prefix.""" + + revision_events = [ + item + for item in records + if item.event_type in {"reslice_recorded", "control_plane_repaired"} + and item.owner == "plan_owner" + ] + first_revision_index = next( + ( + index + for index, item in enumerate(records) + if item.event_type in {"reslice_recorded", "control_plane_repaired"} + and item.owner == "plan_owner" + ), + len(records), + ) + initial = records[:first_revision_index] + initial_phases = { + item.join_ids["phase_id"] for item in initial if item.join_ids["phase_id"] is not None + } + initial_tasks = { + item.join_ids["task_id"] for item in initial if item.join_ids["task_id"] is not None + } + plan_reviews = { + item.join_ids["review_id"] + for item in records + if item.stage == "plan" + and item.event_type == "stage_completed" + and item.join_ids["review_id"] is not None + } + scope_repairs = { + (item.process_id, item.attempt_id) + for item in records + if item.event_type in {"work_returned", "reslice_recorded", "control_plane_repaired"} + and item.finding_class in {"decomposition_gap", "allocation_gap"} + } + task_review_repairs = { + (item.process_id, item.attempt_id) + for item in records + if item.event_type == "work_returned" + and item.finding_class == "implementation_defect" + and item.join_ids["task_id"] is not None + and item.join_ids["review_id"] is not None + } + suite_starts: dict[tuple[str, ...], int] = {} + for item in records: + if item.event_type != "suite_started": + continue + evaluation_id = item.join_ids["evaluation_id"] + key = ( + ("evaluation", evaluation_id) + if evaluation_id is not None + else ( + "scope", + item.process_id, + item.stage, + item.join_ids["plan_id"] or "", + item.join_ids["task_id"] or "", + ) + ) + suite_starts[key] = suite_starts.get(key, 0) + 1 + validation_reruns = sum(max(0, count - 1) for count in suite_starts.values()) + + first_green = min( + ( + _parse_timestamp(item.timestamp) + for item in records + if item.event_type == "suite_completed" + and item.finding_class is None + and item.return_reason is None + ), + default=None, + ) + final_accept = max( + ( + _parse_timestamp(item.timestamp) + for item in records + if item.event_type == "stage_completed" + and item.stage == "integrated_implementation" + and item.join_ids["review_id"] is not None + and item.finding_class is None + and item.return_reason is None + ), + default=None, + ) + latency = None + if first_green is not None and final_accept is not None and final_accept >= first_green: + latency = int((final_accept - first_green).total_seconds() * 1000) + return { + "initial_cardinality": {"phases": len(initial_phases), "tasks": len(initial_tasks)}, + "plan_revisions": len({(item.process_id, item.attempt_id) for item in revision_events}), + "plan_reviews": len(plan_reviews), + "scope_allocation_repairs": len(scope_repairs), + "task_review_repairs": len(task_review_repairs), + "validation_reruns": validation_reruns, + "first_green_to_final_accept_ms": latency, + } + + def redact_event_payload(payload: Mapping[str, object]) -> dict[str, object]: """Fail closed on non-operational content and return the closed public record.""" @@ -345,7 +460,10 @@ def _load_locked(handle) -> list[StageEventV1]: text = raw.decode("utf-8") if not text.endswith("\n"): _fail("WB_STAGE_EVENT_STORE_INVALID") - records = [validate_stage_event(json.loads(line)) for line in text.splitlines()] + records = [ + _validate_stage_event(json.loads(line), allow_derived_economics=True) + for line in text.splitlines() + ] except (UnicodeDecodeError, json.JSONDecodeError, StageEventError, TypeError): _fail("WB_STAGE_EVENT_STORE_INVALID") if len({record.event_id for record in records}) != len(records): @@ -364,6 +482,8 @@ def _load_locked(handle) -> list[StageEventV1]: def append_stage_event(workspace_root: Path, payload: Mapping[str, object]) -> StageEventV1: """Append one event atomically after validating all existing history.""" + if "planning_economics" in payload: + _fail("WB_STAGE_EVENT_ECONOMICS_INVALID") record = validate_stage_event(payload) path = _event_store_path(Path(workspace_root), create_parent=True) flags = os.O_RDWR | os.O_APPEND | os.O_CREAT @@ -389,6 +509,10 @@ def append_stage_event(workspace_root: Path, payload: Mapping[str, object]) -> S _fail("WB_STAGE_EVENT_TIMESTAMP_ORDER_INVALID") if int(record.clocks["wall_ms"]) < int(previous.clocks["wall_ms"]): _fail("WB_STAGE_EVENT_WALL_CLOCK_ORDER_INVALID") + if record.event_type == "stage_completed" and record.stage == "integrated_implementation": + derived = record.to_dict() + derived["planning_economics"] = derive_planning_economics([*existing, record]) + record = _validate_stage_event(derived, allow_derived_economics=True) encoded = (json.dumps(record.to_dict(), sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8") handle.seek(0, os.SEEK_END) handle.write(encoded) diff --git a/tests/test_orchestration_reviews.py b/tests/test_orchestration_reviews.py index 120530e..5ab38d9 100644 --- a/tests/test_orchestration_reviews.py +++ b/tests/test_orchestration_reviews.py @@ -114,7 +114,19 @@ def test_api_001_routes_every_class_to_first_broken_owner( record["obligation_basis"] = "none" validated = validate_contract_instance("reviewFinding", record) assert validated.finding_class == finding_class - assert route_review_verdict(record)["return_to"] == expected[1] + route_context = {} + if finding_class == "allocation_gap": + route_context = { + "affected_region": { + "task_ids": ["task-b01"], + "paths": [], + "interfaces": [], + "validation_oracles": [], + }, + "original_binding_identity": {"binding_id": "binding-b01", "sha256": "1" * 64}, + "original_baseline_identity": {"head": ZERO_TREE, "tree": ZERO_TREE}, + } + assert route_review_verdict(record, **route_context)["return_to"] == expected[1] @pytest.mark.parametrize("field", ["capabilities", "unavailable_evidence"]) @@ -172,14 +184,30 @@ def test_api_001_rejects_unclassified_wrong_layer_and_unauthorized_blocking_advi def test_api_001_reslice_pauses_repeated_expansion_and_preserves_evidence() -> None: - routed = route_review_verdict(finding("allocation_gap"), previous_scope_expansions=1) + routed = route_review_verdict( + finding("allocation_gap"), + previous_scope_expansions=1, + affected_region={ + "task_ids": ["task-b01"], + "paths": [], + "interfaces": [], + "validation_oracles": [], + }, + original_binding_identity={"binding_id": "binding-b01", "sha256": "1" * 64}, + original_baseline_identity={"head": ZERO_TREE, "tree": ZERO_TREE}, + ) assert routed == { "finding_id": "finding-allocation_gap", "first_broken_artifact": "plan", "return_to": "plan_owner", "action": "reslice_plan", "execution_state": "paused_for_reslice", - "affected_region": ["task-b01"], + "affected_region": { + "task_ids": ["task-b01"], + "paths": [], + "interfaces": [], + "validation_oracles": [], + }, "returned_authority_identity": { "artifact_id": "task-b01", "revision": "1", @@ -188,8 +216,8 @@ def test_api_001_reslice_pauses_repeated_expansion_and_preserves_evidence() -> N }, "preserved_evidence_identities": [], "resume_requires": "accepted_repaired_plan_authority", - "preserve_original_binding": True, - "preserve_original_baseline": True, + "original_binding_identity": {"binding_id": "binding-b01", "sha256": "1" * 64}, + "original_baseline_identity": {"head": ZERO_TREE, "tree": ZERO_TREE}, "preserve_valid_work_and_evidence": True, "silent_expansion_allowed": False, } diff --git a/tests/test_wor109_planner_runtime.py b/tests/test_wor109_planner_runtime.py index 68dd308..25ac008 100644 --- a/tests/test_wor109_planner_runtime.py +++ b/tests/test_wor109_planner_runtime.py @@ -6,6 +6,7 @@ from pathlib import Path import pytest +from reviewer_run_fixtures import bind_review_receipt REPO_ROOT = Path(__file__).resolve().parents[1] @@ -21,7 +22,7 @@ resume_plan_return, route_review_verdict, ) -from stage_events import StageEventError, validate_stage_event # noqa: E402 +from stage_events import StageEventError # noqa: E402 ZERO_SHA = "0" * 64 @@ -37,6 +38,23 @@ def identity(artifact_id: str, revision: str = "1") -> dict[str, object]: } +def binding_identity() -> dict[str, str]: + return {"binding_id": "binding-task-003", "sha256": "1" * 64} + + +def baseline_identity() -> dict[str, str]: + return {"head": "2" * 40, "tree": "3" * 40} + + +def affected_region() -> dict[str, list[str]]: + return { + "task_ids": ["task-003"], + "paths": ["scripts/orchestration/review_runtime.py"], + "interfaces": ["API-PD-001"], + "validation_oracles": ["VAL-004"], + } + + def allocation_gap() -> dict[str, object]: artifact, owner, disposition = classify_first_broken_owner("allocation_gap") return { @@ -61,9 +79,9 @@ def allocation_gap() -> dict[str, object]: } -def event() -> dict[str, object]: - return { - "event_id": "event-economics", +def event(event_id: str = "event-economics", **updates: object) -> dict[str, object]: + value: dict[str, object] = { + "event_id": event_id, "timestamp": "2026-09-07T00:00:00Z", "process_id": "process-001", "stage": "integrated_implementation", @@ -88,16 +106,9 @@ def event() -> dict[str, object]: "mutation_epoch": 2, }, "privacy": "operational_metadata_only", - "planning_economics": { - "initial_cardinality": {"phases": 1, "tasks": 6}, - "plan_revisions": 2, - "plan_reviews": 3, - "scope_allocation_repairs": 1, - "task_review_repairs": 2, - "validation_reruns": 4, - "first_green_to_final_accept_ms": 1200, - }, } + value.update(updates) + return value def test_pd_07_returns_only_affected_region_and_preserves_unaffected_evidence() -> None: @@ -105,68 +116,221 @@ def test_pd_07_returns_only_affected_region_and_preserves_unaffected_evidence() routed = route_review_verdict( allocation_gap(), - affected_region=["task-003"], + affected_region=affected_region(), unaffected_evidence_identities=preserved, + original_binding_identity=binding_identity(), + original_baseline_identity=baseline_identity(), ) assert routed["execution_state"] == "paused_for_reslice" - assert routed["affected_region"] == ["task-003"] + assert routed["affected_region"] == affected_region() assert routed["returned_authority_identity"] == identity("plan-001") assert routed["preserved_evidence_identities"] == preserved assert routed["resume_requires"] == "accepted_repaired_plan_authority" - assert routed["preserve_original_binding"] is True - assert routed["preserve_original_baseline"] is True + assert routed["original_binding_identity"] == binding_identity() + assert routed["original_baseline_identity"] == baseline_identity() assert routed["silent_expansion_allowed"] is False -def test_pd_07_resume_waits_for_new_authority_and_exact_preserved_evidence() -> None: +def test_pd_07_resume_waits_for_current_accepted_plan_review_and_exact_preserved_state( + tmp_path: Path, +) -> None: + orch = tmp_path / ".work-bundle/orchestration" + spec = orch / "spec/active/spec.md" + plan = orch / "plan/active/plan.md" + spec.parent.mkdir(parents=True) + plan.parent.mkdir(parents=True) + spec.write_text("---\nid: spec-test\nstatus: verified\n---\nAuthority\n") + plan.write_text("---\nid: plan-001\nstatus: Planned\nsource_spec: [spec-test]\n---\nOriginal\n") preserved = [identity("task-001")] routed = route_review_verdict( allocation_gap(), - affected_region=["task-003"], + affected_region=affected_region(), unaffected_evidence_identities=preserved, + original_binding_identity=binding_identity(), + original_baseline_identity=baseline_identity(), ) + plan.write_text(plan.read_text().replace("Original", "Resliced")) - with pytest.raises(ReviewContractError, match="repaired authority"): + with pytest.raises(ReviewContractError, match="accepted repaired plan-review authority"): resume_plan_return( routed, - accepted_repaired_authority_identity=identity("plan-001"), + workspace_root=tmp_path, + plan_path=plan, + current_binding_identity=binding_identity(), + current_baseline_identity=baseline_identity(), current_unaffected_evidence_identities=preserved, ) + from review_runtime import plan_review_identity + + review = { + "review_id": "review-plan", + "stage": "plan", + "target_identity": plan_review_identity(tmp_path, plan), + "reviewer": { + "agent_id": "reviewer-1", + "capability": "judgment", + "authorship": "none", + "repair_participation": "none", + "decision_participation": "none", + "deliberation_participation": "none", + "context_origin": "direct_source", + }, + "evidence": { + "mode": "direct", + "capabilities": ["source inspection"], + "unavailable_evidence": [], + "commands": [], + "artifacts": [], + }, + "verdict": "accepted", + "findings": [], + "started_at": "2026-09-07T00:00:00Z", + "completed_at": "2026-09-07T00:01:00Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + review = bind_review_receipt(tmp_path, review) + reviews = orch / "reviews" + reviews.mkdir() + (reviews / "plan.json").write_text(json.dumps(review)) + changed = deepcopy(preserved) changed[0]["revision"] = "2" with pytest.raises(ReviewContractError, match="unaffected evidence"): resume_plan_return( routed, - accepted_repaired_authority_identity=identity("plan-001", "2"), + workspace_root=tmp_path, + plan_path=plan, + current_binding_identity=binding_identity(), + current_baseline_identity=baseline_identity(), current_unaffected_evidence_identities=changed, ) + with pytest.raises(ReviewContractError, match="binding identity"): + resume_plan_return( + routed, + workspace_root=tmp_path, + plan_path=plan, + current_binding_identity={**binding_identity(), "sha256": "4" * 64}, + current_baseline_identity=baseline_identity(), + current_unaffected_evidence_identities=preserved, + ) + + with pytest.raises(ReviewContractError, match="baseline identity"): + resume_plan_return( + routed, + workspace_root=tmp_path, + plan_path=plan, + current_binding_identity=binding_identity(), + current_baseline_identity={**baseline_identity(), "tree": "4" * 40}, + current_unaffected_evidence_identities=preserved, + ) + resumed = resume_plan_return( routed, - accepted_repaired_authority_identity=identity("plan-001", "2"), + workspace_root=tmp_path, + plan_path=plan, + current_binding_identity=binding_identity(), + current_baseline_identity=baseline_identity(), current_unaffected_evidence_identities=preserved, ) assert resumed["execution_state"] == "ready_from_repaired_authority" assert resumed["preserved_evidence_identities"] == preserved -@pytest.mark.parametrize("affected_region", [[], ["task-003", "task-003"], [""]]) -def test_pd_07_rejects_unbounded_or_ambiguous_affected_regions(affected_region) -> None: +@pytest.mark.parametrize( + "region", + [ + {}, + {**affected_region(), "task_ids": ["task-003", "task-003"]}, + {**affected_region(), "paths": ["../escape"]}, + {**affected_region(), "validation_oracles": [""]}, + ], +) +def test_pd_07_rejects_unbounded_or_ambiguous_affected_regions(region) -> None: with pytest.raises(ReviewContractError, match="affected region"): - route_review_verdict(allocation_gap(), affected_region=affected_region) - - -def test_pd_08_emits_closed_nonjudgmental_planning_economics() -> None: - value = event() - validated = validate_stage_event(value) + route_review_verdict( + allocation_gap(), + affected_region=region, + original_binding_identity=binding_identity(), + original_baseline_identity=baseline_identity(), + ) - assert validated.to_dict()["planning_economics"] == value["planning_economics"] - # Cardinality is observed, not judged: large and zero values remain valid metadata. - value["planning_economics"]["initial_cardinality"] = {"phases": 0, "tasks": 100_000} - assert validate_stage_event(value).planning_economics["initial_cardinality"]["tasks"] == 100_000 +def test_pd_08_stage_event_path_derives_nonjudgmental_planning_economics(tmp_path: Path) -> None: + from stage_events import append_stage_event, query_stage_events + + events = [ + event("phase", stage="implementation", event_type="stage_started"), + event( + "task-a", + stage="implementation", + event_type="stage_started", + join_ids={**event()["join_ids"], "task_id": "task-a"}, + ), + event( + "task-b", + stage="implementation", + event_type="stage_started", + join_ids={**event()["join_ids"], "task_id": "task-b"}, + ), + event("plan-review", stage="plan", event_type="stage_completed"), + event( + "scope-repair", + event_type="reslice_recorded", + finding_class="allocation_gap", + attempt_id="repair-scope", + ), + event( + "task-repair", + event_type="work_returned", + finding_class="implementation_defect", + attempt_id="repair-task", + join_ids={**event()["join_ids"], "task_id": "task-a"}, + ), + event( + "suite-first", + event_type="suite_started", + attempt_id="validation", + join_ids={**event()["join_ids"], "evaluation_id": "eval-001"}, + ), + event( + "suite-rerun", + event_type="suite_started", + attempt_id="validation-2", + join_ids={**event()["join_ids"], "evaluation_id": "eval-001"}, + ), + event( + "green", + timestamp="2026-09-07T00:00:01Z", + event_type="suite_completed", + attempt_id="validation-2", + join_ids={**event()["join_ids"], "evaluation_id": "eval-001"}, + ), + event( + "accepted", + timestamp="2026-09-07T00:00:02.200Z", + stage="integrated_implementation", + event_type="stage_completed", + attempt_id="final", + ), + ] + result = None + for item in events: + result = append_stage_event(tmp_path, item) + + assert result is not None + assert result.planning_economics == { + "initial_cardinality": {"phases": 1, "tasks": 2}, + "plan_revisions": 1, + "plan_reviews": 1, + "scope_allocation_repairs": 1, + "task_review_repairs": 1, + "validation_reruns": 1, + "first_green_to_final_accept_ms": 1200, + } + assert query_stage_events(tmp_path)[-1].planning_economics == result.planning_economics schema = json.loads( (REPO_ROOT / "references/assets/orchestration/contract/stage-event-v1.schema.json").read_text() @@ -177,11 +341,18 @@ def test_pd_08_emits_closed_nonjudgmental_planning_economics() -> None: assert schema["$defs"]["planningEconomics"]["additionalProperties"] is False -def test_pd_08_economics_is_closed_and_allows_pending_accept_latency() -> None: - value = event() - value["planning_economics"]["first_green_to_final_accept_ms"] = None - assert validate_stage_event(value).planning_economics["first_green_to_final_accept_ms"] is None +def test_pd_08_rejects_caller_injected_economics(tmp_path: Path) -> None: + from stage_events import append_stage_event - value["planning_economics"]["task_count_verdict"] = "too-many" + value = event() + value["planning_economics"] = { + "initial_cardinality": {"phases": 0, "tasks": 100_000}, + "plan_revisions": 0, + "plan_reviews": 0, + "scope_allocation_repairs": 0, + "task_review_repairs": 0, + "validation_reruns": 0, + "first_green_to_final_accept_ms": None, + } with pytest.raises(StageEventError, match="WB_STAGE_EVENT_ECONOMICS_INVALID"): - validate_stage_event(value) + append_stage_event(tmp_path, value) From 66e851eea04709dfa8f9479fea9ab71aa9c9f991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 07:44:54 +0800 Subject: [PATCH 08/48] fix(orchestration): isolate planning economics --- scripts/work-bundle/stage_events.py | 40 ++++++++++---- tests/test_wor109_planner_runtime.py | 83 +++++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 13 deletions(-) diff --git a/scripts/work-bundle/stage_events.py b/scripts/work-bundle/stage_events.py index 1c8522a..14b7554 100644 --- a/scripts/work-bundle/stage_events.py +++ b/scripts/work-bundle/stage_events.py @@ -327,25 +327,37 @@ def validate_stage_event(payload: Mapping[str, object]) -> StageEventV1: def derive_planning_economics( records: Sequence[StageEventV1], + *, + process_id: str, + plan_id: str | None, ) -> dict[str, int | dict[str, int] | None]: - """Derive neutral planning-cost observations from the typed event prefix.""" + """Derive neutral observations from one process and its bound plan prefix.""" - revision_events = [ + if not _is_id(process_id) or (plan_id is not None and not _is_id(plan_id)): + _fail("WB_STAGE_EVENT_ECONOMICS_SCOPE_INVALID") + scoped = [ item for item in records + if item.process_id == process_id + and (plan_id is None or item.join_ids["plan_id"] == plan_id) + ] + + revision_events = [ + item + for item in scoped if item.event_type in {"reslice_recorded", "control_plane_repaired"} and item.owner == "plan_owner" ] first_revision_index = next( ( index - for index, item in enumerate(records) + for index, item in enumerate(scoped) if item.event_type in {"reslice_recorded", "control_plane_repaired"} and item.owner == "plan_owner" ), - len(records), + len(scoped), ) - initial = records[:first_revision_index] + initial = scoped[:first_revision_index] initial_phases = { item.join_ids["phase_id"] for item in initial if item.join_ids["phase_id"] is not None } @@ -354,27 +366,27 @@ def derive_planning_economics( } plan_reviews = { item.join_ids["review_id"] - for item in records + for item in scoped if item.stage == "plan" and item.event_type == "stage_completed" and item.join_ids["review_id"] is not None } scope_repairs = { (item.process_id, item.attempt_id) - for item in records + for item in scoped if item.event_type in {"work_returned", "reslice_recorded", "control_plane_repaired"} and item.finding_class in {"decomposition_gap", "allocation_gap"} } task_review_repairs = { (item.process_id, item.attempt_id) - for item in records + for item in scoped if item.event_type == "work_returned" and item.finding_class == "implementation_defect" and item.join_ids["task_id"] is not None and item.join_ids["review_id"] is not None } suite_starts: dict[tuple[str, ...], int] = {} - for item in records: + for item in scoped: if item.event_type != "suite_started": continue evaluation_id = item.join_ids["evaluation_id"] @@ -395,7 +407,7 @@ def derive_planning_economics( first_green = min( ( _parse_timestamp(item.timestamp) - for item in records + for item in scoped if item.event_type == "suite_completed" and item.finding_class is None and item.return_reason is None @@ -405,7 +417,7 @@ def derive_planning_economics( final_accept = max( ( _parse_timestamp(item.timestamp) - for item in records + for item in scoped if item.event_type == "stage_completed" and item.stage == "integrated_implementation" and item.join_ids["review_id"] is not None @@ -511,7 +523,11 @@ def append_stage_event(workspace_root: Path, payload: Mapping[str, object]) -> S _fail("WB_STAGE_EVENT_WALL_CLOCK_ORDER_INVALID") if record.event_type == "stage_completed" and record.stage == "integrated_implementation": derived = record.to_dict() - derived["planning_economics"] = derive_planning_economics([*existing, record]) + derived["planning_economics"] = derive_planning_economics( + [*existing, record], + process_id=record.process_id, + plan_id=record.join_ids["plan_id"], + ) record = _validate_stage_event(derived, allow_derived_economics=True) encoded = (json.dumps(record.to_dict(), sort_keys=True, separators=(",", ":")) + "\n").encode("utf-8") handle.seek(0, os.SEEK_END) diff --git a/tests/test_wor109_planner_runtime.py b/tests/test_wor109_planner_runtime.py index 25ac008..5e0c642 100644 --- a/tests/test_wor109_planner_runtime.py +++ b/tests/test_wor109_planner_runtime.py @@ -258,7 +258,9 @@ def test_pd_07_rejects_unbounded_or_ambiguous_affected_regions(region) -> None: ) -def test_pd_08_stage_event_path_derives_nonjudgmental_planning_economics(tmp_path: Path) -> None: +def test_pd_08_stage_event_path_isolates_mixed_process_planning_economics( + tmp_path: Path, +) -> None: from stage_events import append_stage_event, query_stage_events events = [ @@ -275,7 +277,39 @@ def test_pd_08_stage_event_path_derives_nonjudgmental_planning_economics(tmp_pat event_type="stage_started", join_ids={**event()["join_ids"], "task_id": "task-b"}, ), + event( + "noise-task", + process_id="process-noise", + stage="implementation", + event_type="stage_started", + join_ids={ + **event()["join_ids"], + "plan_id": "plan-noise", + "phase_id": "phase-noise", + "task_id": "task-noise", + "review_id": "review-noise", + }, + ), + event( + "noise-plan-review", + process_id="process-noise", + stage="plan", + event_type="stage_completed", + join_ids={ + **event()["join_ids"], + "plan_id": "plan-noise", + "review_id": "review-noise", + }, + ), event("plan-review", stage="plan", event_type="stage_completed"), + event( + "noise-scope-repair", + process_id="process-noise", + event_type="reslice_recorded", + finding_class="allocation_gap", + attempt_id="noise-scope", + join_ids={**event()["join_ids"], "plan_id": "plan-noise"}, + ), event( "scope-repair", event_type="reslice_recorded", @@ -289,6 +323,19 @@ def test_pd_08_stage_event_path_derives_nonjudgmental_planning_economics(tmp_pat attempt_id="repair-task", join_ids={**event()["join_ids"], "task_id": "task-a"}, ), + event( + "noise-task-repair", + process_id="process-noise", + event_type="work_returned", + finding_class="implementation_defect", + attempt_id="noise-task-repair", + join_ids={ + **event()["join_ids"], + "plan_id": "plan-noise", + "task_id": "task-noise", + "review_id": "review-noise", + }, + ), event( "suite-first", event_type="suite_started", @@ -301,6 +348,40 @@ def test_pd_08_stage_event_path_derives_nonjudgmental_planning_economics(tmp_pat attempt_id="validation-2", join_ids={**event()["join_ids"], "evaluation_id": "eval-001"}, ), + event( + "noise-suite-first", + process_id="process-noise", + event_type="suite_started", + attempt_id="noise-validation", + join_ids={ + **event()["join_ids"], + "plan_id": "plan-noise", + "evaluation_id": "eval-noise", + }, + ), + event( + "noise-suite-rerun", + process_id="process-noise", + event_type="suite_started", + attempt_id="noise-validation-2", + join_ids={ + **event()["join_ids"], + "plan_id": "plan-noise", + "evaluation_id": "eval-noise", + }, + ), + event( + "noise-green", + process_id="process-noise", + timestamp="2026-09-07T00:00:00.100Z", + event_type="suite_completed", + attempt_id="noise-validation-2", + join_ids={ + **event()["join_ids"], + "plan_id": "plan-noise", + "evaluation_id": "eval-noise", + }, + ), event( "green", timestamp="2026-09-07T00:00:01Z", From d693aa9154b17044d70208bba2777d3f1795adcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 07:48:21 +0800 Subject: [PATCH 09/48] fix(orchestration): require plan-bound economics --- scripts/work-bundle/stage_events.py | 6 +++--- tests/test_wor109_planner_runtime.py | 21 +++++++++++++-------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/scripts/work-bundle/stage_events.py b/scripts/work-bundle/stage_events.py index 14b7554..7240393 100644 --- a/scripts/work-bundle/stage_events.py +++ b/scripts/work-bundle/stage_events.py @@ -329,17 +329,17 @@ def derive_planning_economics( records: Sequence[StageEventV1], *, process_id: str, - plan_id: str | None, + plan_id: str, ) -> dict[str, int | dict[str, int] | None]: """Derive neutral observations from one process and its bound plan prefix.""" - if not _is_id(process_id) or (plan_id is not None and not _is_id(plan_id)): + if not _is_id(process_id) or not _is_id(plan_id): _fail("WB_STAGE_EVENT_ECONOMICS_SCOPE_INVALID") scoped = [ item for item in records if item.process_id == process_id - and (plan_id is None or item.join_ids["plan_id"] == plan_id) + and item.join_ids["plan_id"] == plan_id ] revision_events = [ diff --git a/tests/test_wor109_planner_runtime.py b/tests/test_wor109_planner_runtime.py index 5e0c642..4a3316f 100644 --- a/tests/test_wor109_planner_runtime.py +++ b/tests/test_wor109_planner_runtime.py @@ -258,7 +258,7 @@ def test_pd_07_rejects_unbounded_or_ambiguous_affected_regions(region) -> None: ) -def test_pd_08_stage_event_path_isolates_mixed_process_planning_economics( +def test_pd_08_stage_event_path_isolates_same_process_different_plan_economics( tmp_path: Path, ) -> None: from stage_events import append_stage_event, query_stage_events @@ -279,7 +279,6 @@ def test_pd_08_stage_event_path_isolates_mixed_process_planning_economics( ), event( "noise-task", - process_id="process-noise", stage="implementation", event_type="stage_started", join_ids={ @@ -292,7 +291,6 @@ def test_pd_08_stage_event_path_isolates_mixed_process_planning_economics( ), event( "noise-plan-review", - process_id="process-noise", stage="plan", event_type="stage_completed", join_ids={ @@ -304,7 +302,6 @@ def test_pd_08_stage_event_path_isolates_mixed_process_planning_economics( event("plan-review", stage="plan", event_type="stage_completed"), event( "noise-scope-repair", - process_id="process-noise", event_type="reslice_recorded", finding_class="allocation_gap", attempt_id="noise-scope", @@ -325,7 +322,6 @@ def test_pd_08_stage_event_path_isolates_mixed_process_planning_economics( ), event( "noise-task-repair", - process_id="process-noise", event_type="work_returned", finding_class="implementation_defect", attempt_id="noise-task-repair", @@ -350,7 +346,6 @@ def test_pd_08_stage_event_path_isolates_mixed_process_planning_economics( ), event( "noise-suite-first", - process_id="process-noise", event_type="suite_started", attempt_id="noise-validation", join_ids={ @@ -361,7 +356,6 @@ def test_pd_08_stage_event_path_isolates_mixed_process_planning_economics( ), event( "noise-suite-rerun", - process_id="process-noise", event_type="suite_started", attempt_id="noise-validation-2", join_ids={ @@ -372,7 +366,6 @@ def test_pd_08_stage_event_path_isolates_mixed_process_planning_economics( ), event( "noise-green", - process_id="process-noise", timestamp="2026-09-07T00:00:00.100Z", event_type="suite_completed", attempt_id="noise-validation-2", @@ -422,6 +415,18 @@ def test_pd_08_stage_event_path_isolates_mixed_process_planning_economics( assert schema["$defs"]["planningEconomics"]["additionalProperties"] is False +def test_pd_08_rejects_unbound_integrated_economics_emission(tmp_path: Path) -> None: + from stage_events import append_stage_event + + unbound = event( + "unbound-final", + join_ids={**event()["join_ids"], "plan_id": None}, + ) + + with pytest.raises(StageEventError, match="WB_STAGE_EVENT_ECONOMICS_SCOPE_INVALID"): + append_stage_event(tmp_path, unbound) + + def test_pd_08_rejects_caller_injected_economics(tmp_path: Path) -> None: from stage_events import append_stage_event From 3cf749bbb34b208ca852a5b77bcf26645a50d4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 07:53:50 +0800 Subject: [PATCH 10/48] fix(skills): bound lightweight scope amendments --- references/evals/development/evals.json | 20 +++++++ skills/dev-create-task-plan/SKILL.md | 2 + tests/test_wor109_lightweight.py | 70 +++++++++++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 tests/test_wor109_lightweight.py diff --git a/references/evals/development/evals.json b/references/evals/development/evals.json index 93a5cff..ff24e13 100644 --- a/references/evals/development/evals.json +++ b/references/evals/development/evals.json @@ -84,6 +84,26 @@ "id": "dev-lightweight-capability-floor-extra-ok", "prompt": "Declared Capability is pnpm test, and the agent also uses a stronger model and an extra debugger after meeting that floor.", "expected_output": "Accepts the extra capability because Capability is a floor; stronger models, extra tools, or extra investigation are allowed, and weaker capability than the floor is not." + }, + { + "id": "dev-lightweight-pre-mutation-one-file-amendment", + "prompt": "Before mutation, source grounding shows that a lightweight plan must add one exact file owned by the same implementation owner; purpose, accepted authority, expected delta, impact radius, ownership, validation boundary, and completion claim are materially unchanged.", + "expected_output": "Amends Files.Modify once with the exact additional path and records the supporting evidence before the first write, while keeping the same disposable lightweight plan." + }, + { + "id": "dev-lightweight-amendment-after-mutation", + "prompt": "A lightweight task has already mutated an authorized file when it discovers one more file that would otherwise satisfy the bounded amendment conditions.", + "expected_output": "Does not amend the mutation envelope after mutation has begun; stops and escalates to full orchestration with the concrete scope evidence." + }, + { + "id": "dev-lightweight-material-under-decomposition", + "prompt": "Execution reveals that the proposed extra file introduces a new production owner and an independent validation boundary.", + "expected_output": "Treats the task as materially under-decomposed and escalates to full orchestration instead of repeatedly expanding the lightweight plan." + }, + { + "id": "dev-lightweight-amendment-lane-separation", + "prompt": "A pre-mutation one-file amendment remains same-owner and mechanically bounded, but the agent proposes adding an executor result, task state, review package, and archive record for assurance.", + "expected_output": "Allows only the exact bounded Files.Modify amendment and rejects heavy lifecycle artifacts; the lightweight lane remains one disposable plan." } ] } diff --git a/skills/dev-create-task-plan/SKILL.md b/skills/dev-create-task-plan/SKILL.md index f1215f4..eb0a093 100644 --- a/skills/dev-create-task-plan/SKILL.md +++ b/skills/dev-create-task-plan/SKILL.md @@ -62,6 +62,8 @@ Use exactly this structure: `Files.Read` and `Files.Test` are initial evidence anchors; `Files.Modify` is the mutation envelope. Additional bounded reads and tests are allowed. Writes outside `Files.Modify` remain unauthorized without an explicit plan amendment or escalation. +Before the first write, one explicit plan amendment may add exactly one additional path to `Files.Modify` when it has the same implementation owner and purpose, decision authority, expected delta, impact radius, ownership, validation boundary, and completion claim remain materially unchanged. Record the exact path and supporting evidence in the existing disposable plan. The amendment must not be repeated or made after mutation begins. If new evidence makes the task materially under-decomposed—a new production or lifecycle owner, independent validation boundary, wide impact, API or workflow decision, second repository, or barrier or convergence topology—stop and escalate to full orchestration. + Capability is a floor. Stronger models, extra tools, or extra investigation are allowed. Weaker capability than the floor is not. Completion evidence must record: the exact claim, the command or observation used, the observed result, comparison to the pre-edit baseline for the claimed delta, remaining blockers, and knowledge disposition. Intended checks without observed results are not completion evidence. diff --git a/tests/test_wor109_lightweight.py b/tests/test_wor109_lightweight.py new file mode 100644 index 0000000..7388cd3 --- /dev/null +++ b/tests/test_wor109_lightweight.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import json +from pathlib import Path + + +REPO_ROOT = Path(__file__).resolve().parents[1] +SKILL_PATH = REPO_ROOT / "skills" / "dev-create-task-plan" / "SKILL.md" +EVALS_PATH = REPO_ROOT / "references" / "evals" / "development" / "evals.json" + + +def test_lightweight_plan_allows_one_bounded_pre_mutation_path_amendment() -> None: + text = SKILL_PATH.read_text(encoding="utf-8") + + for token in [ + "Before the first write", + "exactly one additional path", + "same implementation owner", + "purpose, decision authority, expected delta, impact radius, ownership, validation boundary, and completion claim", + "materially unchanged", + "exact path", + "supporting evidence", + ]: + assert token in text + + +def test_lightweight_plan_escalates_material_under_decomposition() -> None: + text = SKILL_PATH.read_text(encoding="utf-8") + + for token in [ + "materially under-decomposed", + "new production or lifecycle owner", + "independent validation boundary", + "wide impact", + "API or workflow decision", + "second repository", + "barrier or convergence topology", + "full orchestration", + "must not be repeated", + ]: + assert token in text + + +def test_lightweight_scope_pressure_evals_cover_wor109_boundaries() -> None: + cases = json.loads(EVALS_PATH.read_text(encoding="utf-8"))["evals"] + by_id = {case["id"]: case for case in cases} + required = { + "dev-lightweight-pre-mutation-one-file-amendment", + "dev-lightweight-amendment-after-mutation", + "dev-lightweight-material-under-decomposition", + "dev-lightweight-amendment-lane-separation", + } + + assert required <= by_id.keys() + selected = " ".join( + by_id[case_id][field] + for case_id in sorted(required) + for field in ("prompt", "expected_output") + ) + for token in [ + "exact additional path", + "before the first write", + "already mutated", + "new production owner", + "independent validation boundary", + "materially under-decomposed", + "executor result", + "one disposable plan", + ]: + assert token in selected From b130344f6ec8c4734e20717d823637494fed4887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 12:03:18 +0800 Subject: [PATCH 11/48] test(orchestration): converge WOR-109 acceptance gates --- evals/wor109/contracts-v1.schema.json | 20 ++++ evals/wor109/fixtures.json | 20 ++++ evals/wor109/migration-impact.json | 30 ++++++ evals/wor109/verify.py | 132 ++++++++++++++++++++++++++ tests/test_wor109_closure.py | 39 ++++++++ 5 files changed, 241 insertions(+) create mode 100644 evals/wor109/contracts-v1.schema.json create mode 100644 evals/wor109/fixtures.json create mode 100644 evals/wor109/migration-impact.json create mode 100644 evals/wor109/verify.py create mode 100644 tests/test_wor109_closure.py diff --git a/evals/wor109/contracts-v1.schema.json b/evals/wor109/contracts-v1.schema.json new file mode 100644 index 0000000..65d88e7 --- /dev/null +++ b/evals/wor109/contracts-v1.schema.json @@ -0,0 +1,20 @@ +{ + "$id": "urn:work-bundle:wor109:closure-contracts:v1", + "type": "object", + "additionalProperties": false, + "required": ["fixture"], + "properties": { + "fixture": { + "type": "object", + "additionalProperties": false, + "required": ["id", "scenario", "oracle", "source_ids", "pytest_node"], + "properties": { + "id": {"type": "string", "pattern": "^PD-(0[1-9]|1[0-4])$"}, + "scenario": {"type": "string"}, + "oracle": {"type": "string"}, + "source_ids": {"type": "array", "items": {"type": "string"}}, + "pytest_node": {"type": "string"} + } + } + } +} diff --git a/evals/wor109/fixtures.json b/evals/wor109/fixtures.json new file mode 100644 index 0000000..a63575b --- /dev/null +++ b/evals/wor109/fixtures.json @@ -0,0 +1,20 @@ +{ + "contract": "wor109-closure-fixtures-v1", + "evaluation_id": "wor109-review-stable-orchestration-v1", + "fixtures": [ + {"id":"PD-01","scenario":"coarse plan versus larger plan with evidenced independent runtime entry points","oracle":"larger plan allowed only for independently repairable seams; cardinality has no reward","source_ids":["REQ-PD-001","REQ-PD-004"],"pytest_node":"tests/test_wor109_planner_contracts.py::test_heavy_planning_decomposes_from_production_and_repair_seams"}, + {"id":"PD-02","scenario":"task owns a review/runtime helper but omits its known production lifecycle surface","oracle":"planner contracts require an authoritative production owner","source_ids":["REQ-PD-001","REQ-PD-002"],"pytest_node":"tests/test_wor109_accepted_result.py::test_shared_scope_canonicalizer_normalizes_equivalent_paths_and_rejects_unsafe"}, + {"id":"PD-03","scenario":"scheduler/ownership helper and unit fixture exist but executor acceptance path is unassigned","oracle":"production acceptance authority is explicitly bound","source_ids":["REQ-PD-002"],"pytest_node":"tests/test_wor109_accepted_result.py::test_accepted_result_is_deterministic_current_authority_not_handoff_history"}, + {"id":"PD-04","scenario":"two production entry points implement one requirement but fail and repair independently","oracle":"independent repair identities remain distinct","source_ids":["REQ-PD-001","REQ-PD-005"],"pytest_node":"tests/test_wor109_accepted_result.py::test_actual_accepted_repair_review_mode_and_frontier_are_digest_authority"}, + {"id":"PD-05","scenario":"single-file mechanical change has one scope, oracle, owner, and repair frontier","oracle":"coherent mechanical increments are not micro-tasked","source_ids":["REQ-PD-004","REQ-PD-005"],"pytest_node":"tests/test_wor109_lifecycle.py::test_current_accepted_result_does_not_read_handoff_or_replay_validation"}, + {"id":"PD-06","scenario":"two independent producers feed one convergence consumer","oracle":"actual barriers and convergence are represented","source_ids":["REQ-PD-005"],"pytest_node":"tests/test_wor109_planner_runtime.py::test_pd_08_stage_event_path_isolates_same_process_different_plan_economics"}, + {"id":"PD-07","scenario":"load-bearing telemetry/oracle field is omitted solely for brevity","oracle":"complete nonredundant authority retains load-bearing fields","source_ids":["REQ-SPEC-001"],"pytest_node":"tests/test_wor109_lifecycle.py::test_task_completion_reuses_current_accepted_result_without_handoff"}, + {"id":"PD-08","scenario":"one plan proceeds through reviews, scope repairs, task repairs, reruns, and final acceptance","oracle":"planning economics are emitted without cardinality judgment","source_ids":["REQ-PD-006"],"pytest_node":"tests/test_wor109_planner_runtime.py::test_pd_08_rejects_caller_injected_economics"}, + {"id":"PD-09","scenario":"review proves a coherent task needs a separately owned production path or validation boundary","oracle":"affected region returns to plan owner while unaffected evidence is preserved","source_ids":["REQ-PD-003","CON-003"],"pytest_node":"tests/test_wor109_planner_runtime.py::test_pd_07_returns_only_affected_region_and_preserves_unaffected_evidence"}, + {"id":"PD-10","scenario":"tasks are proposed only for hypothetical future defects without a current seam","oracle":"speculative fragments are not added","source_ids":["REQ-PD-004"],"pytest_node":"tests/test_wor109_lightweight.py::test_lightweight_plan_escalates_material_under_decomposition"}, + {"id":"PD-11","scenario":"lightweight task discovers one same-owner file with unchanged authority and validation","oracle":"one exact pre-mutation amendment remains lightweight","source_ids":["REQ-LW-001","REQ-LW-003"],"pytest_node":"tests/test_wor109_lightweight.py::test_lightweight_plan_allows_one_bounded_pre_mutation_path_amendment"}, + {"id":"PD-12","scenario":"lightweight work discovers a new owner, boundary, wide impact, API decision, repository, or barrier","oracle":"material scope pressure escalates to full orchestration","source_ids":["REQ-LW-002"],"pytest_node":"tests/test_wor109_lifecycle.py::test_dependency_and_phase_gates_consume_only_current_accepted_results"}, + {"id":"PD-13","scenario":"normal eligible lightweight change runs after WOR-109","oracle":"one disposable plan remains isolated from heavy machinery","source_ids":["REQ-LW-003"],"pytest_node":"tests/test_wor109_lightweight.py::test_lightweight_scope_pressure_evals_cover_wor109_boundaries"}, + {"id":"PD-14","scenario":"equivalent under-decomposition evidence reaches heavy and lightweight lanes","oracle":"heavy reslices and lightweight escalates without silent widening","source_ids":["REQ-PD-003","REQ-LW-002"],"pytest_node":"tests/test_wor109_planner_runtime.py::test_pd_07_rejects_unbounded_or_ambiguous_affected_regions"} + ] +} diff --git a/evals/wor109/migration-impact.json b/evals/wor109/migration-impact.json new file mode 100644 index 0000000..5986f46 --- /dev/null +++ b/evals/wor109/migration-impact.json @@ -0,0 +1,30 @@ +{ + "contract": "wor109-migration-impact-v1", + "issue": "WOR-109", + "baseline": {"commit": "cfa089f0d2ed211b98d049eb37bfcdccb8091516", "tree": "12e4a696c3caf991654f0b9ac9ef40594699c8d4"}, + "accepted_worktree": {"commit": "3cf749bbb34b208ca852a5b77bcf26645a50d4cb", "tree": "e343bf27f41996bd8189949ae6749f48abc09652", "status": "clean"}, + "changed_surfaces": { + "added": {"skills": [], "rules": [], "contracts": [], "runtime_operations": [], "artifact_semantics": [], "evaluations": ["evals/wor109/contracts-v1.schema.json", "evals/wor109/fixtures.json", "evals/wor109/migration-impact.json", "evals/wor109/verify.py", "tests/test_wor109_accepted_result.py", "tests/test_wor109_closure.py", "tests/test_wor109_lifecycle.py", "tests/test_wor109_lightweight.py", "tests/test_wor109_planner_contracts.py", "tests/test_wor109_planner_runtime.py"]}, + "modified": {"skills": ["skills/dev-create-task-plan/SKILL.md", "skills/orch-create-implementation-plan/SKILL.md", "skills/orch-create-specification/SKILL.md"], "rules": ["rules/orchestration/orch-artifact-authoring.md"], "contracts": ["references/assets/orchestration/contract/plan-v1.md", "references/assets/orchestration/contract/stage-event-v1.schema.json"], "runtime_operations": ["scripts/orchestration/dispatcher.py", "scripts/orchestration/execution_context.py", "scripts/orchestration/plans.py", "scripts/orchestration/review_runtime.py", "scripts/orchestration/task_ownership.py", "scripts/work-bundle/stage_events.py"], "artifact_semantics": ["evals/wor105/components/native-transition-record.yaml", "references/assets/orchestration/workflow.md", "references/evals/development/evals.json", "references/evals/orchestration/evals.json", "tests/test_orchestration_reviews.py", "tests/test_orchestration_skill_rule_boundary.py", "tests/test_orchestration_workflow_contracts.py", "tests/test_wor108_context_projection.py"], "evaluations": []}, + "deleted": {"skills": [], "rules": [], "contracts": [], "runtime_operations": [], "artifact_semantics": [], "evaluations": []} + }, + "semantic_deltas": { + "acceptance_authority": {"source_ids": ["REQ-AR-001", "REQ-AR-002"], "affected_paths": ["scripts/orchestration/execution_context.py", "tests/test_wor109_accepted_result.py"]}, + "planner_reslice": {"source_ids": ["REQ-PD-001", "REQ-PD-003"], "affected_paths": ["skills/orch-create-implementation-plan/SKILL.md", "scripts/orchestration/plans.py"]}, + "specification_wording": {"source_ids": ["REQ-SPEC-001"], "affected_paths": ["skills/orch-create-specification/SKILL.md"]}, + "lightweight_scope": {"source_ids": ["REQ-LW-001", "REQ-LW-002"], "affected_paths": ["skills/dev-create-task-plan/SKILL.md"]}, + "historical_identity": {"source_ids": ["REQ-ID-001"], "affected_paths": ["tests/test_wor109_lifecycle.py"]} + }, + "parity_owners": { + "WOR-76": {"status": "likely", "navigation": "Review accepted-result authority and lifecycle projection consumers."}, + "WOR-78": {"status": "likely", "navigation": "Review planner decomposition and bounded reslice contracts."}, + "WOR-81": {"status": "likely", "navigation": "Review lightweight scope-control lane semantics."}, + "WOR-82": {"status": "likely", "navigation": "Review deterministic integrated evaluation and closure gates."} + }, + "epoch1_evidence": { + "reusable": ["WOR-108 closure fixtures", "baseline identity cfa089f0d2ed211b98d049eb37bfcdccb8091516"], + "invalidated": [{"evidence_id": "transient handoff acceptance evidence", "reason": "acceptance authority is now persisted once", "replacement": "accepted-result projection digests"}] + }, + "evaluation": {"fixture_ids": ["PD-01", "PD-02", "PD-03", "PD-04", "PD-05", "PD-06", "PD-07", "PD-08", "PD-09", "PD-10", "PD-11", "PD-12", "PD-13", "PD-14"], "verifier_output_digest": "9789fd03efdec6253e13d8c6a95e678c81765f394732d98775b13ff66e629462"}, + "exclusions": {"work-bundle-mcp": {"authorized": false}, "WOR-107": {"authorized": false}, "WOR-79": {"authorized": false}, "Step 00 mutation": {"authorized": false}, "migration execution": {"authorized": false}} +} diff --git a/evals/wor109/verify.py b/evals/wor109/verify.py new file mode 100644 index 0000000..138e5f7 --- /dev/null +++ b/evals/wor109/verify.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +"""Independent deterministic verifier for the WOR-109 closure package.""" +from __future__ import annotations + +import argparse +import hashlib +import json +import re +import subprocess +from pathlib import Path +from typing import Any + +EVAL_ROOT = Path(__file__).resolve().parent +REPO_ROOT = EVAL_ROOT.parents[1] +BASELINE = {"commit": "cfa089f0d2ed211b98d049eb37bfcdccb8091516", "tree": "12e4a696c3caf991654f0b9ac9ef40594699c8d4"} +EXPECTED_IDS = tuple(f"PD-{i:02d}" for i in range(1, 15)) +SOURCE_IDS = {"REQ-PD-001", "REQ-PD-002", "REQ-PD-003", "REQ-PD-004", "REQ-PD-005", "REQ-PD-006", "REQ-SPEC-001", "REQ-LW-001", "REQ-LW-002", "REQ-LW-003", "CON-003"} +FIXTURE_KEYS = {"id", "scenario", "oracle", "source_ids", "pytest_node"} +MIGRATION_KEYS = {"contract", "issue", "baseline", "accepted_worktree", "changed_surfaces", "semantic_deltas", "parity_owners", "epoch1_evidence", "evaluation", "exclusions"} +CATEGORIES = {"skills", "rules", "contracts", "runtime_operations", "artifact_semantics", "evaluations"} +OPERATIONS = {"added", "modified", "deleted"} +NODE = re.compile(r"^tests/test_wor109_[a-z_]+\.py::test_[a-z0-9_]+$") + +class VerificationError(RuntimeError): + pass + +def _load(path: Path) -> dict[str, Any]: + value = json.loads(path.read_text(encoding="utf-8")) + if not isinstance(value, dict): + raise VerificationError(f"object required: {path}") + return value + +def _verify_schema(path: Path) -> None: + schema = _load(path) + if schema.get("$id") != "urn:work-bundle:wor109:closure-contracts:v1" or schema.get("additionalProperties") is not False: + raise VerificationError("closure schema identity or closed shape mismatch") + fixture = schema.get("properties", {}).get("fixture", {}) + if fixture.get("additionalProperties") is not False or set(fixture.get("required", [])) != FIXTURE_KEYS: + raise VerificationError("fixture schema is not closed") + +def _verify_fixtures(path: Path) -> tuple[str, ...]: + package = _load(path) + if set(package) != {"contract", "evaluation_id", "fixtures"} or package["contract"] != "wor109-closure-fixtures-v1" or package["evaluation_id"] != "wor109-review-stable-orchestration-v1": + raise VerificationError("fixture package identity or shape mismatch") + fixtures = package["fixtures"] + if not isinstance(fixtures, list) or tuple(item.get("id") for item in fixtures if isinstance(item, dict)) != EXPECTED_IDS: + raise VerificationError("fixture IDs must be exactly PD-01..PD-14") + nodes: set[str] = set() + for item in fixtures: + if set(item) != FIXTURE_KEYS or not item["scenario"] or not item["oracle"]: + raise VerificationError(f"fixture closed shape mismatch: {item.get('id')}") + if not isinstance(item["source_ids"], list) or not item["source_ids"] or not set(item["source_ids"]).issubset(SOURCE_IDS): + raise VerificationError(f"fixture source authority mismatch: {item['id']}") + node = item["pytest_node"] + if not NODE.fullmatch(node) or node in nodes: + raise VerificationError(f"invalid or duplicate pytest node: {node}") + nodes.add(node) + test_path, function = node.split("::") + source = REPO_ROOT / test_path + if not source.is_file() or f"def {function}(" not in source.read_text(encoding="utf-8"): + raise VerificationError(f"pytest oracle does not resolve: {node}") + return EXPECTED_IDS + +def _git_changed() -> dict[str, str]: + proc = subprocess.run(["git", "-C", str(REPO_ROOT), "diff", "--name-status", BASELINE["commit"], "HEAD", "--"], capture_output=True, text=True, check=False) + if proc.returncode: + raise VerificationError("baseline delta unavailable") + result: dict[str, str] = {} + for line in proc.stdout.splitlines(): + status, path = line.split("\t", 1) + result[path] = {"A": "added", "M": "modified", "D": "deleted"}.get(status, "modified") + return result + +def _verify_migration(path: Path, fixture_ids: tuple[str, ...]) -> int: + manifest = _load(path) + if set(manifest) != MIGRATION_KEYS or manifest["contract"] != "wor109-migration-impact-v1" or manifest["issue"] != "WOR-109" or manifest["baseline"] != BASELINE: + raise VerificationError("migration identity or closed shape mismatch") + accepted = manifest["accepted_worktree"] + if set(accepted) != {"commit", "tree", "status"} or accepted["commit"] != "3cf749bbb34b208ca852a5b77bcf26645a50d4cb" or accepted["tree"] != "e343bf27f41996bd8189949ae6749f48abc09652": + raise VerificationError("accepted pre-commit worktree mismatch") + surfaces = manifest["changed_surfaces"] + if set(surfaces) != OPERATIONS or any(set(group) != CATEGORIES for group in surfaces.values()): + raise VerificationError("changed surface dimensions mismatch") + listed: dict[str, str] = {} + for op, groups in surfaces.items(): + for category, paths in groups.items(): + if not isinstance(paths, list) or paths != sorted(set(paths)): + raise VerificationError("changed surfaces must be sorted and unique") + for rel in paths: + if rel in listed or (op != "deleted" and not (REPO_ROOT / rel).is_file()): + raise VerificationError(f"invalid changed surface: {rel}") + listed[rel] = op + if listed != _git_changed(): + raise VerificationError("changed surface completeness mismatch") + if set(manifest["semantic_deltas"]) != {"acceptance_authority", "planner_reslice", "specification_wording", "lightweight_scope", "historical_identity"}: + raise VerificationError("semantic delta rows mismatch") + for row in manifest["semantic_deltas"].values(): + if set(row) != {"source_ids", "affected_paths"} or not row["source_ids"] or not row["affected_paths"]: + raise VerificationError("semantic delta row incomplete") + if set(manifest["parity_owners"]) != {"WOR-76", "WOR-78", "WOR-81", "WOR-82"} or any(set(row) != {"status", "navigation"} or row["status"] != "likely" for row in manifest["parity_owners"].values()): + raise VerificationError("parity owner rows mismatch") + evidence = manifest["epoch1_evidence"] + if set(evidence) != {"reusable", "invalidated"} or not isinstance(evidence["reusable"], list) or not isinstance(evidence["invalidated"], list): + raise VerificationError("epoch-1 evidence partition mismatch") + for row in evidence["invalidated"]: + if set(row) != {"evidence_id", "reason", "replacement"} or not row["reason"] or not row["replacement"]: + raise VerificationError("invalidated evidence requires reason and replacement") + evaluation = manifest["evaluation"] + if set(evaluation) != {"fixture_ids", "verifier_output_digest"} or tuple(evaluation["fixture_ids"]) != fixture_ids or not re.fullmatch(r"[0-9a-f]{64}", evaluation["verifier_output_digest"]): + raise VerificationError("evaluation binding mismatch") + exclusions = manifest["exclusions"] + if set(exclusions) != {"work-bundle-mcp", "WOR-107", "WOR-79", "Step 00 mutation", "migration execution"} or any(row != {"authorized": False} for row in exclusions.values()): + raise VerificationError("exclusion authorization mismatch") + return len(listed) + +def verify(fixtures_path: Path = EVAL_ROOT / "fixtures.json", migration_path: Path = EVAL_ROOT / "migration-impact.json", schema_path: Path = EVAL_ROOT / "contracts-v1.schema.json") -> dict[str, Any]: + _verify_schema(schema_path) + ids = _verify_fixtures(fixtures_path) + changed = _verify_migration(migration_path, ids) + return {"evaluation_id": "wor109-review-stable-orchestration-v1", "fixtures": len(ids), "changed_surfaces": changed, "verdict": "accepted"} + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--fixtures", type=Path, default=EVAL_ROOT / "fixtures.json") + parser.add_argument("--migration-impact", type=Path, default=EVAL_ROOT / "migration-impact.json") + parser.add_argument("--schema", type=Path, default=EVAL_ROOT / "contracts-v1.schema.json") + args = parser.parse_args() + print(json.dumps(verify(args.fixtures, args.migration_impact, args.schema), sort_keys=True)) + return 0 + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tests/test_wor109_closure.py b/tests/test_wor109_closure.py new file mode 100644 index 0000000..ad2899e --- /dev/null +++ b/tests/test_wor109_closure.py @@ -0,0 +1,39 @@ +from __future__ import annotations + +import copy +import importlib.util +import json +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[1] +EVAL = ROOT / "evals" / "wor109" +spec = importlib.util.spec_from_file_location("wor109_verify", EVAL / "verify.py") +assert spec and spec.loader +verifier = importlib.util.module_from_spec(spec) +spec.loader.exec_module(verifier) + +def test_wor109_closure_has_exact_fixture_and_manifest_identity() -> None: + assert verifier.verify() == {"evaluation_id": "wor109-review-stable-orchestration-v1", "fixtures": 14, "changed_surfaces": 30, "verdict": "accepted"} + +def test_wor109_fixture_registry_is_closed_and_unique() -> None: + payload = json.loads((EVAL / "fixtures.json").read_text(encoding="utf-8")) + assert tuple(row["id"] for row in payload["fixtures"]) == tuple(f"PD-{i:02d}" for i in range(1, 15)) + assert len({row["pytest_node"] for row in payload["fixtures"]}) == 14 + +def test_wor109_tampered_fixture_is_rejected(tmp_path: Path) -> None: + payload = json.loads((EVAL / "fixtures.json").read_text(encoding="utf-8")) + payload["fixtures"] = payload["fixtures"][:-1] + path = tmp_path / "fixtures.json" + path.write_text(json.dumps(payload), encoding="utf-8") + with pytest.raises(verifier.VerificationError, match="exactly PD-01..PD-14"): + verifier.verify(fixtures_path=path) + +def test_wor109_tampered_manifest_is_rejected(tmp_path: Path) -> None: + payload = copy.deepcopy(json.loads((EVAL / "migration-impact.json").read_text(encoding="utf-8"))) + payload["exclusions"]["WOR-107"]["authorized"] = True + path = tmp_path / "migration-impact.json" + path.write_text(json.dumps(payload), encoding="utf-8") + with pytest.raises(verifier.VerificationError, match="exclusion authorization"): + verifier.verify(migration_path=path) From 8205127a3ec0fb1aa7e22535cb58ab3bd696eec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 12:14:16 +0800 Subject: [PATCH 12/48] fix(evals): reconcile WOR-109 migration gate --- evals/wor105/freeze-manifest.json | 2 +- evals/wor109/migration-impact.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/evals/wor105/freeze-manifest.json b/evals/wor105/freeze-manifest.json index 6cb30cb..5a190df 100644 --- a/evals/wor105/freeze-manifest.json +++ b/evals/wor105/freeze-manifest.json @@ -72,7 +72,7 @@ "components": { "profile": { "path": "evals/wor105/components/native-transition-record.yaml", - "sha256": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a" + "sha256": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72" }, "fixtures": { "path": "evals/wor105/fixtures", diff --git a/evals/wor109/migration-impact.json b/evals/wor109/migration-impact.json index 5986f46..1f37f26 100644 --- a/evals/wor109/migration-impact.json +++ b/evals/wor109/migration-impact.json @@ -5,7 +5,7 @@ "accepted_worktree": {"commit": "3cf749bbb34b208ca852a5b77bcf26645a50d4cb", "tree": "e343bf27f41996bd8189949ae6749f48abc09652", "status": "clean"}, "changed_surfaces": { "added": {"skills": [], "rules": [], "contracts": [], "runtime_operations": [], "artifact_semantics": [], "evaluations": ["evals/wor109/contracts-v1.schema.json", "evals/wor109/fixtures.json", "evals/wor109/migration-impact.json", "evals/wor109/verify.py", "tests/test_wor109_accepted_result.py", "tests/test_wor109_closure.py", "tests/test_wor109_lifecycle.py", "tests/test_wor109_lightweight.py", "tests/test_wor109_planner_contracts.py", "tests/test_wor109_planner_runtime.py"]}, - "modified": {"skills": ["skills/dev-create-task-plan/SKILL.md", "skills/orch-create-implementation-plan/SKILL.md", "skills/orch-create-specification/SKILL.md"], "rules": ["rules/orchestration/orch-artifact-authoring.md"], "contracts": ["references/assets/orchestration/contract/plan-v1.md", "references/assets/orchestration/contract/stage-event-v1.schema.json"], "runtime_operations": ["scripts/orchestration/dispatcher.py", "scripts/orchestration/execution_context.py", "scripts/orchestration/plans.py", "scripts/orchestration/review_runtime.py", "scripts/orchestration/task_ownership.py", "scripts/work-bundle/stage_events.py"], "artifact_semantics": ["evals/wor105/components/native-transition-record.yaml", "references/assets/orchestration/workflow.md", "references/evals/development/evals.json", "references/evals/orchestration/evals.json", "tests/test_orchestration_reviews.py", "tests/test_orchestration_skill_rule_boundary.py", "tests/test_orchestration_workflow_contracts.py", "tests/test_wor108_context_projection.py"], "evaluations": []}, + "modified": {"skills": ["skills/dev-create-task-plan/SKILL.md", "skills/orch-create-implementation-plan/SKILL.md", "skills/orch-create-specification/SKILL.md"], "rules": ["rules/orchestration/orch-artifact-authoring.md"], "contracts": ["references/assets/orchestration/contract/plan-v1.md", "references/assets/orchestration/contract/stage-event-v1.schema.json"], "runtime_operations": ["scripts/orchestration/dispatcher.py", "scripts/orchestration/execution_context.py", "scripts/orchestration/plans.py", "scripts/orchestration/review_runtime.py", "scripts/work-bundle/stage_events.py"], "artifact_semantics": ["evals/wor105/components/native-transition-record.yaml", "evals/wor105/freeze-manifest.json", "references/assets/orchestration/workflow.md", "references/evals/development/evals.json", "references/evals/orchestration/evals.json", "tests/test_orchestration_reviews.py", "tests/test_orchestration_skill_rule_boundary.py", "tests/test_orchestration_workflow_contracts.py", "tests/test_wor108_context_projection.py"], "evaluations": []}, "deleted": {"skills": [], "rules": [], "contracts": [], "runtime_operations": [], "artifact_semantics": [], "evaluations": []} }, "semantic_deltas": { From a39923ab040bcf63dbd2b87c123060d08cf14189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 12:15:06 +0800 Subject: [PATCH 13/48] fix(evals): complete WOR-109 impact union --- evals/wor109/migration-impact.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evals/wor109/migration-impact.json b/evals/wor109/migration-impact.json index 1f37f26..81f9e67 100644 --- a/evals/wor109/migration-impact.json +++ b/evals/wor109/migration-impact.json @@ -5,7 +5,7 @@ "accepted_worktree": {"commit": "3cf749bbb34b208ca852a5b77bcf26645a50d4cb", "tree": "e343bf27f41996bd8189949ae6749f48abc09652", "status": "clean"}, "changed_surfaces": { "added": {"skills": [], "rules": [], "contracts": [], "runtime_operations": [], "artifact_semantics": [], "evaluations": ["evals/wor109/contracts-v1.schema.json", "evals/wor109/fixtures.json", "evals/wor109/migration-impact.json", "evals/wor109/verify.py", "tests/test_wor109_accepted_result.py", "tests/test_wor109_closure.py", "tests/test_wor109_lifecycle.py", "tests/test_wor109_lightweight.py", "tests/test_wor109_planner_contracts.py", "tests/test_wor109_planner_runtime.py"]}, - "modified": {"skills": ["skills/dev-create-task-plan/SKILL.md", "skills/orch-create-implementation-plan/SKILL.md", "skills/orch-create-specification/SKILL.md"], "rules": ["rules/orchestration/orch-artifact-authoring.md"], "contracts": ["references/assets/orchestration/contract/plan-v1.md", "references/assets/orchestration/contract/stage-event-v1.schema.json"], "runtime_operations": ["scripts/orchestration/dispatcher.py", "scripts/orchestration/execution_context.py", "scripts/orchestration/plans.py", "scripts/orchestration/review_runtime.py", "scripts/work-bundle/stage_events.py"], "artifact_semantics": ["evals/wor105/components/native-transition-record.yaml", "evals/wor105/freeze-manifest.json", "references/assets/orchestration/workflow.md", "references/evals/development/evals.json", "references/evals/orchestration/evals.json", "tests/test_orchestration_reviews.py", "tests/test_orchestration_skill_rule_boundary.py", "tests/test_orchestration_workflow_contracts.py", "tests/test_wor108_context_projection.py"], "evaluations": []}, + "modified": {"skills": ["skills/dev-create-task-plan/SKILL.md", "skills/orch-create-implementation-plan/SKILL.md", "skills/orch-create-specification/SKILL.md"], "rules": ["rules/orchestration/orch-artifact-authoring.md"], "contracts": ["references/assets/orchestration/contract/plan-v1.md", "references/assets/orchestration/contract/stage-event-v1.schema.json"], "runtime_operations": ["scripts/orchestration/dispatcher.py", "scripts/orchestration/execution_context.py", "scripts/orchestration/plans.py", "scripts/orchestration/review_runtime.py", "scripts/orchestration/task_ownership.py", "scripts/work-bundle/stage_events.py"], "artifact_semantics": ["evals/wor105/components/native-transition-record.yaml", "evals/wor105/freeze-manifest.json", "references/assets/orchestration/workflow.md", "references/evals/development/evals.json", "references/evals/orchestration/evals.json", "tests/test_orchestration_reviews.py", "tests/test_orchestration_skill_rule_boundary.py", "tests/test_orchestration_workflow_contracts.py", "tests/test_wor108_context_projection.py"], "evaluations": []}, "deleted": {"skills": [], "rules": [], "contracts": [], "runtime_operations": [], "artifact_semantics": [], "evaluations": []} }, "semantic_deltas": { From ff8c8b823634f77febd719dcb79fb81840ddd6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 12:15:18 +0800 Subject: [PATCH 14/48] fix(evals): refresh WOR-109 closure count --- tests/test_wor109_closure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_wor109_closure.py b/tests/test_wor109_closure.py index ad2899e..adcccf8 100644 --- a/tests/test_wor109_closure.py +++ b/tests/test_wor109_closure.py @@ -15,7 +15,7 @@ spec.loader.exec_module(verifier) def test_wor109_closure_has_exact_fixture_and_manifest_identity() -> None: - assert verifier.verify() == {"evaluation_id": "wor109-review-stable-orchestration-v1", "fixtures": 14, "changed_surfaces": 30, "verdict": "accepted"} + assert verifier.verify() == {"evaluation_id": "wor109-review-stable-orchestration-v1", "fixtures": 14, "changed_surfaces": 31, "verdict": "accepted"} def test_wor109_fixture_registry_is_closed_and_unique() -> None: payload = json.loads((EVAL / "fixtures.json").read_text(encoding="utf-8")) From 5ed76f5c0a55ea481df5b29e18cdd6065b8632d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 12:57:09 +0800 Subject: [PATCH 15/48] fix(evals): include WOR-83 parity navigation --- evals/wor109/migration-impact.json | 3 ++- evals/wor109/verify.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/evals/wor109/migration-impact.json b/evals/wor109/migration-impact.json index 81f9e67..2339944 100644 --- a/evals/wor109/migration-impact.json +++ b/evals/wor109/migration-impact.json @@ -19,7 +19,8 @@ "WOR-76": {"status": "likely", "navigation": "Review accepted-result authority and lifecycle projection consumers."}, "WOR-78": {"status": "likely", "navigation": "Review planner decomposition and bounded reslice contracts."}, "WOR-81": {"status": "likely", "navigation": "Review lightweight scope-control lane semantics."}, - "WOR-82": {"status": "likely", "navigation": "Review deterministic integrated evaluation and closure gates."} + "WOR-82": {"status": "likely", "navigation": "Review deterministic integrated evaluation and closure gates."}, + "WOR-83": {"status": "likely", "navigation": "Review planning telemetry and diagnostics emitted by stage-event convergence."} }, "epoch1_evidence": { "reusable": ["WOR-108 closure fixtures", "baseline identity cfa089f0d2ed211b98d049eb37bfcdccb8091516"], diff --git a/evals/wor109/verify.py b/evals/wor109/verify.py index 138e5f7..890fc55 100644 --- a/evals/wor109/verify.py +++ b/evals/wor109/verify.py @@ -97,7 +97,9 @@ def _verify_migration(path: Path, fixture_ids: tuple[str, ...]) -> int: for row in manifest["semantic_deltas"].values(): if set(row) != {"source_ids", "affected_paths"} or not row["source_ids"] or not row["affected_paths"]: raise VerificationError("semantic delta row incomplete") - if set(manifest["parity_owners"]) != {"WOR-76", "WOR-78", "WOR-81", "WOR-82"} or any(set(row) != {"status", "navigation"} or row["status"] != "likely" for row in manifest["parity_owners"].values()): + telemetry_changed = any(path in listed for path in ("scripts/work-bundle/stage_events.py", "scripts/orchestration/review_runtime.py")) + expected_parity = {"WOR-76", "WOR-78", "WOR-81", "WOR-82"} | ({"WOR-83"} if telemetry_changed else set()) + if set(manifest["parity_owners"]) != expected_parity or any(set(row) != {"status", "navigation"} or row["status"] != "likely" for row in manifest["parity_owners"].values()): raise VerificationError("parity owner rows mismatch") evidence = manifest["epoch1_evidence"] if set(evidence) != {"reusable", "invalidated"} or not isinstance(evidence["reusable"], list) or not isinstance(evidence["invalidated"], list): From 66ba53012883a87faecd5ef16532efed1f3a3b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 13:01:48 +0800 Subject: [PATCH 16/48] test(orchestration): converge historical evaluation identities --- evals/wor105/results.jsonl | 24 ++++++++++++------------ evals/wor108/migration-impact.json | 21 ++++++++++++++++++++- evals/wor109/migration-impact.json | 2 +- tests/test_wor105_native_transition.py | 8 ++++++++ 4 files changed, 41 insertions(+), 14 deletions(-) diff --git a/evals/wor105/results.jsonl b/evals/wor105/results.jsonl index 1c4c932..e985f2f 100644 --- a/evals/wor105/results.jsonl +++ b/evals/wor105/results.jsonl @@ -1,12 +1,12 @@ -{"actual_decision": "deny_mutation_and_protected_reads_allow_bounded_evidence", "adjudication_sha256": "f256137ea263da8806b4b1a33a3a7510a48e3c6672c8262dc75aa4a26ffc71e4", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-01:mutation-denial:1", "event:ADV-01:mutation-denial:2"], "expected_decision": "deny_mutation_and_protected_reads_allow_bounded_evidence", "fixture_id": "ADV-01", "fixture_sha256": "52e6a3f83058f820fe01343e5b009940842874f05bdf47aa2e99ea60a27127bb", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"allowed_read_output_sha256": "1dc07dc04e672df4c66bca312cc8aa0fd3845e9117eb9c3722c805ba49cad7c4", "control_sentinel_after_sha256": "1e9c7b842690803f3f25a996afcd18348e571ef93083e2094f7c533dbdef0397", "control_sentinel_before_sha256": "1e9c7b842690803f3f25a996afcd18348e571ef93083e2094f7c533dbdef0397", "denial_classes": ["permission_denied", "permission_denied", "permission_denied", "permission_denied", "permission_denied"], "event_ids": ["event:ADV-01:mutation-denial:1", "event:ADV-01:mutation-denial:2"], "source_sentinel_after_sha256": "b2271b177e020061b04fc194bf5ec85a9cdbfc9e74074b73b52d76a5fc2af85a", "source_sentinel_before_sha256": "b2271b177e020061b04fc194bf5ec85a9cdbfc9e74074b73b52d76a5fc2af85a", "validator_output_sha256": "4caeb0c30f3bd412655341504b3b564ce92c160c17e419c5251d8b61a9c8f259"}, "raw_evidence_sha256": "4f2253688acd2c03346731731e6b10a9b3f95c991f91125ad00acf7e01e01d7d", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "pause_and_reslice_after_second_expansion", "adjudication_sha256": "0c312e858a1dabac1efc47526ba05dd56a25c4351574d543104213fe4fef87b6", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-02:1"], "expected_decision": "pause_and_reslice_after_second_expansion", "fixture_id": "ADV-02", "fixture_sha256": "78fe6de27c7833336f6883a73dc61aaa0b15b37735ae639e9061e071e0dfcd40", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"binding_state": "repair_owned", "expansion_event_ids": ["event:ADV-02:expand:1", "event:ADV-02:expand:2"], "original_evidence_sha256": "dbe1e53e72d77259941220006b4dabe76e616d22ae79027474eacbc4312e67a0", "reslice_artifact_sha256": "cf7240e355ba73ddef7d7376e813c8fb4d4d462b40409bdf9a933fc241dd3d6e", "return_owner": "plan_owner"}, "raw_evidence_sha256": "29d9edb9048ae76009eb3a23e5c83665d2d3dd8335ca9e24f5c8399a42a47662", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "reject_and_route_allocation_gap_to_plan_reslice", "adjudication_sha256": "51468752b9157242d57bb4bb7c23937578afbd08933b94f7fca98b86ab8fabae", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-03:1"], "expected_decision": "reject_and_route_allocation_gap_to_plan_reslice", "fixture_id": "ADV-03", "fixture_sha256": "490ebdd8890adb88bd52a6cd24a54d02bdc6750d0a3f9cc63e78cc6a2d68d260", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"canonical_finding_sha256": "b18f342fdb3df678ce2df6d512fcdb70739adce75c9cd0cb38d713a8435d20f5", "rejected_record_sha256": "c3800c532270ebf1766019851e6edc82a13b9ce0901668bb0f4b7fa99bfd6df0", "validation_error_code": "finding_route_mismatch"}, "raw_evidence_sha256": "5ce46fcc3d48798e1aeceb6b01e206612cf0e45066968d898e8d8d601fc16c6c", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "reject_blocking_and_record_nonblocking_advisory", "adjudication_sha256": "8f68aa67e626e52e4d9e901dda96c65a95db4f72ced495e1d6a76ea587c442b3", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-04:1"], "expected_decision": "reject_blocking_and_record_nonblocking_advisory", "fixture_id": "ADV-04", "fixture_sha256": "19db36abd1deb09f95179b572e2c559509aea0a2607aa1a04cd4b5af7db291ef", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"advisory_id": "advisory:ADV-04", "stage_state_after_sha256": "7f11ee6a1f8fc67e343ff9ede27759613559df69e153fefa0540dc55281a2df6", "stage_state_before_sha256": "7f11ee6a1f8fc67e343ff9ede27759613559df69e153fefa0540dc55281a2df6", "validation_error_code": "blocking_basis_required"}, "raw_evidence_sha256": "741437d7d693ed2aaeba457eab8f7dfa216cb52e26a1e43245882052439b2213", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "stale_run_append_invalidation_preserve_raw_evidence", "adjudication_sha256": "a4fd0509b02c28e16a858b9c277227da3585eafe562992594f5c6f8683c9dc44", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-05:1"], "expected_decision": "stale_run_append_invalidation_preserve_raw_evidence", "fixture_id": "ADV-05", "fixture_sha256": "42251cea32fd52341f87067be6bbc4194da7139b65b823dcf230b11f50baa3f7", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"invalidation_id": "invalidation:ADV-05", "new_digest": "da9456aece01c51674e58791a55e81a9357e4c6dd14de537038a668b1c26c2ec", "old_digest": "de0043aa39f8969804ba2e21f6abbc4a5eb50bf22177c9ce60e8807ec1fe671b", "raw_response_after_sha256": "1a16053dccca94ba3e07bf3f1119c0a3387364cecc59752c2ab4a7d734f9de4f", "raw_response_before_sha256": "1a16053dccca94ba3e07bf3f1119c0a3387364cecc59752c2ab4a7d734f9de4f", "raw_trace_after_sha256": "68245e9a19559d24fa246ec87f100351c7e2f97b88374254956d08611ef3a84d", "raw_trace_before_sha256": "68245e9a19559d24fa246ec87f100351c7e2f97b88374254956d08611ef3a84d", "stale_run_id": "run:ADV-05"}, "raw_evidence_sha256": "0821a85ca0f258bd769bc848136420c9c46035322e9e1cc85726da32c8648ef7", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "preserve_product_observation_update_packaging_only", "adjudication_sha256": "b784bb58f6b27be4f92782776647fb3985d99a5cc07b0f9f4c3e276e8c5bde20", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-06:1"], "expected_decision": "preserve_product_observation_update_packaging_only", "fixture_id": "ADV-06", "fixture_sha256": "f5241e874cd284c3e41ecab7e2790d265a8ca7f0e53785d9bb151ebeefb16d6e", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"observation_after_sha256": "b5729bf53e81f9a32161b5e412cdc1ef24259d833f1cbc448caed5f0733dc8ea", "observation_before_sha256": "b5729bf53e81f9a32161b5e412cdc1ef24259d833f1cbc448caed5f0733dc8ea", "packaging_after": "3333333333333333333333333333333333333333", "packaging_before": "2222222222222222222222222222222222222222", "product_tree_after": "1111111111111111111111111111111111111111", "product_tree_before": "1111111111111111111111111111111111111111", "valid": true}, "raw_evidence_sha256": "4ee5d2cf39b9f696bf9ca32a6c246de0c0488656d3dc6c4b65a08fbba28a2676", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "mark_review_stale_and_remove_stage_credit", "adjudication_sha256": "6d06a92a211f065ba1216c44062d06fa78b49cb914d2d81cfa5a2a656344e64c", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-07:1"], "expected_decision": "mark_review_stale_and_remove_stage_credit", "fixture_id": "ADV-07", "fixture_sha256": "f9446f50289c62fd1115d89d2121576950174cbad68bb4e76cdd8cef1a962d47", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"countable_stage_reviews": 0, "review_id": "review:ADV-07", "staleness_reason": "target_identity_changed", "target_after_sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "target_before_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, "raw_evidence_sha256": "fc968bdfbc46c514d3c0965e0b19d7601af03bbf35c40c9ac67fb2e2e76238e4", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "execute_once_and_reuse_observation", "adjudication_sha256": "4216f0afd1e4634a240f548cc0ecc0872aeb8363c53d43e7f518211d6e84e94c", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-08:1"], "expected_decision": "execute_once_and_reuse_observation", "fixture_id": "ADV-08", "fixture_sha256": "b8b07a687c7d492ad983b88a72b837f18ae50b6c384aa394a732c60e940bd01e", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"observation_id": "observation:430c6f98184ff990ddfd", "request_ids": ["request:ADV-08:1", "request:ADV-08:2"], "reuse_of": "observation:430c6f98184ff990ddfd", "subprocess_invocation_count": 1}, "raw_evidence_sha256": "6ba81e81dfb301c7c481b9987e7ea1c5fbd55c362e2fa63b79aa83dbc4e0ad86", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "deny_release_preserve_owner_reason_history", "adjudication_sha256": "3abf36eb3dc3ccc6f1ff4616598825e2174ab8774b2b0a5931508c347a58462d", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-09:1"], "expected_decision": "deny_release_preserve_owner_reason_history", "fixture_id": "ADV-09", "fixture_sha256": "5a347c746583f3bc1175b50aa5b2fdf8250e07a929206d777a48bdb7e50ca70f", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"after_snapshot_sha256": "a34c0d2062af8cb91d18210f9008b41fbc70bf54d0149bea6f6c2d22eb116179", "before_snapshot_sha256": "a34c0d2062af8cb91d18210f9008b41fbc70bf54d0149bea6f6c2d22eb116179", "denial_event_ids": ["event:ADV-09:repair", "event:ADV-09:rereview"], "original_owner": "repair_owned", "original_reason": "binding retained by active repair owner"}, "raw_evidence_sha256": "79fe7d310cfde0ffccfdf4004232ff2d47be0f14ecac5031f69fff34077e1d33", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "reject_each_and_require_fresh_reviewer", "adjudication_sha256": "510e35f8ad9d7560c08c15820c68e04a65f6bad545c201b2d23856a418ba6008", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-10:1"], "expected_decision": "reject_each_and_require_fresh_reviewer", "fixture_id": "ADV-10", "fixture_sha256": "7ea520dcefa78d9348f354b1a9dcc9726798a0759b0b768d1ae476770731c673", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"countable_stage_reviews": 0, "rejected_review_ids": ["review:ADV-10:authorship", "review:ADV-10:repair_participation", "review:ADV-10:decision_participation", "review:ADV-10:deliberation_participation"], "validation_error_codes": ["reviewer_not_independent", "reviewer_not_independent", "reviewer_not_independent", "reviewer_not_independent"]}, "raw_evidence_sha256": "8b72d990c3945f0cccb06a95663374d559c4711da54a895dca554d179912da4c", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "route_validation_oracle_defect_without_product_rollback", "adjudication_sha256": "921bb49dae8fcf29f6bbe956ca146f8d91b6c16f6c7a93f4ba5ee9687ee2609d", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-11:1"], "expected_decision": "route_validation_oracle_defect_without_product_rollback", "fixture_id": "ADV-11", "fixture_sha256": "4520983615ce735ab5cd6ba1729e464085e0af0ec2f152fcf32872e201041ef7", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"byte_oracle_failure_sha256": "3513611273bb3e74c5c8a7224ddae25322904f08dd42e595a47ed8c9120aed73", "product_revision_after": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "product_revision_before": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "public_contract_test_output_sha256": "ec996444a9a62cde22b0ee5e1713a679f65f9aac74f285bd0ecdb9c04ff01e00", "routed_finding_sha256": "11d26026ee1ecc06e4418dc9e850d5e09d185ab8638bdd6edbfa41a60d14336b"}, "raw_evidence_sha256": "02671b359b2c0d23649cfd9e99874413da954bd8b4ff3bb08d9b65e3b844bd24", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop", "adjudication_sha256": "dd2390df12ef6f7363a688d9e96424889975274a8be62c9cf99cab34136500fb", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "0d39d8709d8b4884a93384a51829d147e465dbf674e2c122d324d67c1283205a", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-12:1"], "expected_decision": "reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop", "fixture_id": "ADV-12", "fixture_sha256": "7f29996db2939960f308f7bfe72e139e3eded355804a72860b98add5d0ce0e18", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"checkout_absent": true, "first_apply_state_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "member_snapshot_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "origin_absent": true, "replay_state_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "validation_error_code": "placeholder_remote_forbidden"}, "raw_evidence_sha256": "83ea6e80a7289ec65e87c0c694c11b8903bff0a8339085ff31a8391b2790be50", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "deny_mutation_and_protected_reads_allow_bounded_evidence", "adjudication_sha256": "f256137ea263da8806b4b1a33a3a7510a48e3c6672c8262dc75aa4a26ffc71e4", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-01:mutation-denial:1", "event:ADV-01:mutation-denial:2"], "expected_decision": "deny_mutation_and_protected_reads_allow_bounded_evidence", "fixture_id": "ADV-01", "fixture_sha256": "52e6a3f83058f820fe01343e5b009940842874f05bdf47aa2e99ea60a27127bb", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"allowed_read_output_sha256": "1dc07dc04e672df4c66bca312cc8aa0fd3845e9117eb9c3722c805ba49cad7c4", "control_sentinel_after_sha256": "1e9c7b842690803f3f25a996afcd18348e571ef93083e2094f7c533dbdef0397", "control_sentinel_before_sha256": "1e9c7b842690803f3f25a996afcd18348e571ef93083e2094f7c533dbdef0397", "denial_classes": ["permission_denied", "permission_denied", "permission_denied", "permission_denied", "permission_denied"], "event_ids": ["event:ADV-01:mutation-denial:1", "event:ADV-01:mutation-denial:2"], "source_sentinel_after_sha256": "b2271b177e020061b04fc194bf5ec85a9cdbfc9e74074b73b52d76a5fc2af85a", "source_sentinel_before_sha256": "b2271b177e020061b04fc194bf5ec85a9cdbfc9e74074b73b52d76a5fc2af85a", "validator_output_sha256": "4caeb0c30f3bd412655341504b3b564ce92c160c17e419c5251d8b61a9c8f259"}, "raw_evidence_sha256": "4f2253688acd2c03346731731e6b10a9b3f95c991f91125ad00acf7e01e01d7d", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "pause_and_reslice_after_second_expansion", "adjudication_sha256": "0c312e858a1dabac1efc47526ba05dd56a25c4351574d543104213fe4fef87b6", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-02:1"], "expected_decision": "pause_and_reslice_after_second_expansion", "fixture_id": "ADV-02", "fixture_sha256": "78fe6de27c7833336f6883a73dc61aaa0b15b37735ae639e9061e071e0dfcd40", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"binding_state": "repair_owned", "expansion_event_ids": ["event:ADV-02:expand:1", "event:ADV-02:expand:2"], "original_evidence_sha256": "dbe1e53e72d77259941220006b4dabe76e616d22ae79027474eacbc4312e67a0", "reslice_artifact_sha256": "cf7240e355ba73ddef7d7376e813c8fb4d4d462b40409bdf9a933fc241dd3d6e", "return_owner": "plan_owner"}, "raw_evidence_sha256": "29d9edb9048ae76009eb3a23e5c83665d2d3dd8335ca9e24f5c8399a42a47662", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "reject_and_route_allocation_gap_to_plan_reslice", "adjudication_sha256": "51468752b9157242d57bb4bb7c23937578afbd08933b94f7fca98b86ab8fabae", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-03:1"], "expected_decision": "reject_and_route_allocation_gap_to_plan_reslice", "fixture_id": "ADV-03", "fixture_sha256": "490ebdd8890adb88bd52a6cd24a54d02bdc6750d0a3f9cc63e78cc6a2d68d260", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"canonical_finding_sha256": "b18f342fdb3df678ce2df6d512fcdb70739adce75c9cd0cb38d713a8435d20f5", "rejected_record_sha256": "c3800c532270ebf1766019851e6edc82a13b9ce0901668bb0f4b7fa99bfd6df0", "validation_error_code": "finding_route_mismatch"}, "raw_evidence_sha256": "5ce46fcc3d48798e1aeceb6b01e206612cf0e45066968d898e8d8d601fc16c6c", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "reject_blocking_and_record_nonblocking_advisory", "adjudication_sha256": "8f68aa67e626e52e4d9e901dda96c65a95db4f72ced495e1d6a76ea587c442b3", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-04:1"], "expected_decision": "reject_blocking_and_record_nonblocking_advisory", "fixture_id": "ADV-04", "fixture_sha256": "19db36abd1deb09f95179b572e2c559509aea0a2607aa1a04cd4b5af7db291ef", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"advisory_id": "advisory:ADV-04", "stage_state_after_sha256": "7f11ee6a1f8fc67e343ff9ede27759613559df69e153fefa0540dc55281a2df6", "stage_state_before_sha256": "7f11ee6a1f8fc67e343ff9ede27759613559df69e153fefa0540dc55281a2df6", "validation_error_code": "blocking_basis_required"}, "raw_evidence_sha256": "741437d7d693ed2aaeba457eab8f7dfa216cb52e26a1e43245882052439b2213", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "stale_run_append_invalidation_preserve_raw_evidence", "adjudication_sha256": "a4fd0509b02c28e16a858b9c277227da3585eafe562992594f5c6f8683c9dc44", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-05:1"], "expected_decision": "stale_run_append_invalidation_preserve_raw_evidence", "fixture_id": "ADV-05", "fixture_sha256": "42251cea32fd52341f87067be6bbc4194da7139b65b823dcf230b11f50baa3f7", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"invalidation_id": "invalidation:ADV-05", "new_digest": "da9456aece01c51674e58791a55e81a9357e4c6dd14de537038a668b1c26c2ec", "old_digest": "de0043aa39f8969804ba2e21f6abbc4a5eb50bf22177c9ce60e8807ec1fe671b", "raw_response_after_sha256": "1a16053dccca94ba3e07bf3f1119c0a3387364cecc59752c2ab4a7d734f9de4f", "raw_response_before_sha256": "1a16053dccca94ba3e07bf3f1119c0a3387364cecc59752c2ab4a7d734f9de4f", "raw_trace_after_sha256": "68245e9a19559d24fa246ec87f100351c7e2f97b88374254956d08611ef3a84d", "raw_trace_before_sha256": "68245e9a19559d24fa246ec87f100351c7e2f97b88374254956d08611ef3a84d", "stale_run_id": "run:ADV-05"}, "raw_evidence_sha256": "0821a85ca0f258bd769bc848136420c9c46035322e9e1cc85726da32c8648ef7", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "preserve_product_observation_update_packaging_only", "adjudication_sha256": "b784bb58f6b27be4f92782776647fb3985d99a5cc07b0f9f4c3e276e8c5bde20", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-06:1"], "expected_decision": "preserve_product_observation_update_packaging_only", "fixture_id": "ADV-06", "fixture_sha256": "f5241e874cd284c3e41ecab7e2790d265a8ca7f0e53785d9bb151ebeefb16d6e", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"observation_after_sha256": "b5729bf53e81f9a32161b5e412cdc1ef24259d833f1cbc448caed5f0733dc8ea", "observation_before_sha256": "b5729bf53e81f9a32161b5e412cdc1ef24259d833f1cbc448caed5f0733dc8ea", "packaging_after": "3333333333333333333333333333333333333333", "packaging_before": "2222222222222222222222222222222222222222", "product_tree_after": "1111111111111111111111111111111111111111", "product_tree_before": "1111111111111111111111111111111111111111", "valid": true}, "raw_evidence_sha256": "4ee5d2cf39b9f696bf9ca32a6c246de0c0488656d3dc6c4b65a08fbba28a2676", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "mark_review_stale_and_remove_stage_credit", "adjudication_sha256": "6d06a92a211f065ba1216c44062d06fa78b49cb914d2d81cfa5a2a656344e64c", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-07:1"], "expected_decision": "mark_review_stale_and_remove_stage_credit", "fixture_id": "ADV-07", "fixture_sha256": "f9446f50289c62fd1115d89d2121576950174cbad68bb4e76cdd8cef1a962d47", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"countable_stage_reviews": 0, "review_id": "review:ADV-07", "staleness_reason": "target_identity_changed", "target_after_sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "target_before_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, "raw_evidence_sha256": "fc968bdfbc46c514d3c0965e0b19d7601af03bbf35c40c9ac67fb2e2e76238e4", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "execute_once_and_reuse_observation", "adjudication_sha256": "4216f0afd1e4634a240f548cc0ecc0872aeb8363c53d43e7f518211d6e84e94c", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-08:1"], "expected_decision": "execute_once_and_reuse_observation", "fixture_id": "ADV-08", "fixture_sha256": "b8b07a687c7d492ad983b88a72b837f18ae50b6c384aa394a732c60e940bd01e", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"observation_id": "observation:430c6f98184ff990ddfd", "request_ids": ["request:ADV-08:1", "request:ADV-08:2"], "reuse_of": "observation:430c6f98184ff990ddfd", "subprocess_invocation_count": 1}, "raw_evidence_sha256": "6ba81e81dfb301c7c481b9987e7ea1c5fbd55c362e2fa63b79aa83dbc4e0ad86", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "deny_release_preserve_owner_reason_history", "adjudication_sha256": "3abf36eb3dc3ccc6f1ff4616598825e2174ab8774b2b0a5931508c347a58462d", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-09:1"], "expected_decision": "deny_release_preserve_owner_reason_history", "fixture_id": "ADV-09", "fixture_sha256": "5a347c746583f3bc1175b50aa5b2fdf8250e07a929206d777a48bdb7e50ca70f", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"after_snapshot_sha256": "a34c0d2062af8cb91d18210f9008b41fbc70bf54d0149bea6f6c2d22eb116179", "before_snapshot_sha256": "a34c0d2062af8cb91d18210f9008b41fbc70bf54d0149bea6f6c2d22eb116179", "denial_event_ids": ["event:ADV-09:repair", "event:ADV-09:rereview"], "original_owner": "repair_owned", "original_reason": "binding retained by active repair owner"}, "raw_evidence_sha256": "79fe7d310cfde0ffccfdf4004232ff2d47be0f14ecac5031f69fff34077e1d33", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "reject_each_and_require_fresh_reviewer", "adjudication_sha256": "510e35f8ad9d7560c08c15820c68e04a65f6bad545c201b2d23856a418ba6008", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-10:1"], "expected_decision": "reject_each_and_require_fresh_reviewer", "fixture_id": "ADV-10", "fixture_sha256": "7ea520dcefa78d9348f354b1a9dcc9726798a0759b0b768d1ae476770731c673", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"countable_stage_reviews": 0, "rejected_review_ids": ["review:ADV-10:authorship", "review:ADV-10:repair_participation", "review:ADV-10:decision_participation", "review:ADV-10:deliberation_participation"], "validation_error_codes": ["reviewer_not_independent", "reviewer_not_independent", "reviewer_not_independent", "reviewer_not_independent"]}, "raw_evidence_sha256": "8b72d990c3945f0cccb06a95663374d559c4711da54a895dca554d179912da4c", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "route_validation_oracle_defect_without_product_rollback", "adjudication_sha256": "921bb49dae8fcf29f6bbe956ca146f8d91b6c16f6c7a93f4ba5ee9687ee2609d", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-11:1"], "expected_decision": "route_validation_oracle_defect_without_product_rollback", "fixture_id": "ADV-11", "fixture_sha256": "4520983615ce735ab5cd6ba1729e464085e0af0ec2f152fcf32872e201041ef7", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"byte_oracle_failure_sha256": "3513611273bb3e74c5c8a7224ddae25322904f08dd42e595a47ed8c9120aed73", "product_revision_after": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "product_revision_before": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "public_contract_test_output_sha256": "ec996444a9a62cde22b0ee5e1713a679f65f9aac74f285bd0ecdb9c04ff01e00", "routed_finding_sha256": "11d26026ee1ecc06e4418dc9e850d5e09d185ab8638bdd6edbfa41a60d14336b"}, "raw_evidence_sha256": "02671b359b2c0d23649cfd9e99874413da954bd8b4ff3bb08d9b65e3b844bd24", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} +{"actual_decision": "reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop", "adjudication_sha256": "dd2390df12ef6f7363a688d9e96424889975274a8be62c9cf99cab34136500fb", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-12:1"], "expected_decision": "reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop", "fixture_id": "ADV-12", "fixture_sha256": "7f29996db2939960f308f7bfe72e139e3eded355804a72860b98add5d0ce0e18", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"checkout_absent": true, "first_apply_state_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "member_snapshot_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "origin_absent": true, "replay_state_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "validation_error_code": "placeholder_remote_forbidden"}, "raw_evidence_sha256": "83ea6e80a7289ec65e87c0c694c11b8903bff0a8339085ff31a8391b2790be50", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} diff --git a/evals/wor108/migration-impact.json b/evals/wor108/migration-impact.json index e12b3a8..28b11cd 100644 --- a/evals/wor108/migration-impact.json +++ b/evals/wor108/migration-impact.json @@ -9,6 +9,8 @@ "changed_surfaces": { "public_contracts": [ "references/assets/orchestration/contract/handoff-executor-result-v1.md", + "references/assets/orchestration/contract/plan-v1.md", + "references/assets/orchestration/contract/stage-event-v1.schema.json", "references/assets/orchestration/contract/stage-review-v1.schema.json", "references/assets/orchestration/contract/task-v1.md", "references/assets/orchestration/workflow.md" @@ -17,9 +19,13 @@ "references/assets/template/AGENTS.md", "references/assets/template/bootstrap.yaml", "references/assets/template/project.yaml", + "rules/orchestration/orch-artifact-authoring.md", "rules/orchestration/orch-handoff-required.md", "rules/work-bundle/wb-project-context-preflight.md", + "skills/dev-create-task-plan/SKILL.md", "skills/orch-create-handoff/SKILL.md", + "skills/orch-create-implementation-plan/SKILL.md", + "skills/orch-create-specification/SKILL.md", "skills/orch-doctor/SKILL.md", "skills/orch-execute-plan/SKILL.md", "skills/wb-initialize-project/SKILL.md" @@ -46,18 +52,31 @@ "evals/wor108/fixtures.json", "evals/wor108/migration-impact.json", "evals/wor108/verify.py", + "evals/wor109/contracts-v1.schema.json", + "evals/wor109/fixtures.json", + "evals/wor109/migration-impact.json", + "evals/wor109/verify.py", + "references/evals/development/evals.json", "references/evals/orchestration/evals.json", "tests/test_control_plane_v4.py", "tests/test_multi_repository_member.py", "tests/test_orchestration_execution_context.py", + "tests/test_orchestration_reviews.py", "tests/test_orchestration_skill_rule_boundary.py", "tests/test_orchestration_workflow_contracts.py", "tests/test_project_initialization.py", "tests/test_registry_layout_migration.py", + "tests/test_wor105_native_transition.py", "tests/test_wor108_closure.py", "tests/test_wor108_context_projection.py", "tests/test_wor108_review_frontier.py", - "tests/test_wor108_subagent_ownership.py" + "tests/test_wor108_subagent_ownership.py", + "tests/test_wor109_accepted_result.py", + "tests/test_wor109_closure.py", + "tests/test_wor109_lifecycle.py", + "tests/test_wor109_lightweight.py", + "tests/test_wor109_planner_contracts.py", + "tests/test_wor109_planner_runtime.py" ] }, "evaluation_identities": [ diff --git a/evals/wor109/migration-impact.json b/evals/wor109/migration-impact.json index 2339944..beb7bde 100644 --- a/evals/wor109/migration-impact.json +++ b/evals/wor109/migration-impact.json @@ -5,7 +5,7 @@ "accepted_worktree": {"commit": "3cf749bbb34b208ca852a5b77bcf26645a50d4cb", "tree": "e343bf27f41996bd8189949ae6749f48abc09652", "status": "clean"}, "changed_surfaces": { "added": {"skills": [], "rules": [], "contracts": [], "runtime_operations": [], "artifact_semantics": [], "evaluations": ["evals/wor109/contracts-v1.schema.json", "evals/wor109/fixtures.json", "evals/wor109/migration-impact.json", "evals/wor109/verify.py", "tests/test_wor109_accepted_result.py", "tests/test_wor109_closure.py", "tests/test_wor109_lifecycle.py", "tests/test_wor109_lightweight.py", "tests/test_wor109_planner_contracts.py", "tests/test_wor109_planner_runtime.py"]}, - "modified": {"skills": ["skills/dev-create-task-plan/SKILL.md", "skills/orch-create-implementation-plan/SKILL.md", "skills/orch-create-specification/SKILL.md"], "rules": ["rules/orchestration/orch-artifact-authoring.md"], "contracts": ["references/assets/orchestration/contract/plan-v1.md", "references/assets/orchestration/contract/stage-event-v1.schema.json"], "runtime_operations": ["scripts/orchestration/dispatcher.py", "scripts/orchestration/execution_context.py", "scripts/orchestration/plans.py", "scripts/orchestration/review_runtime.py", "scripts/orchestration/task_ownership.py", "scripts/work-bundle/stage_events.py"], "artifact_semantics": ["evals/wor105/components/native-transition-record.yaml", "evals/wor105/freeze-manifest.json", "references/assets/orchestration/workflow.md", "references/evals/development/evals.json", "references/evals/orchestration/evals.json", "tests/test_orchestration_reviews.py", "tests/test_orchestration_skill_rule_boundary.py", "tests/test_orchestration_workflow_contracts.py", "tests/test_wor108_context_projection.py"], "evaluations": []}, + "modified": {"skills": ["skills/dev-create-task-plan/SKILL.md", "skills/orch-create-implementation-plan/SKILL.md", "skills/orch-create-specification/SKILL.md"], "rules": ["rules/orchestration/orch-artifact-authoring.md"], "contracts": ["references/assets/orchestration/contract/plan-v1.md", "references/assets/orchestration/contract/stage-event-v1.schema.json"], "runtime_operations": ["scripts/orchestration/dispatcher.py", "scripts/orchestration/execution_context.py", "scripts/orchestration/plans.py", "scripts/orchestration/review_runtime.py", "scripts/orchestration/task_ownership.py", "scripts/work-bundle/stage_events.py"], "artifact_semantics": ["evals/wor105/components/native-transition-record.yaml", "evals/wor105/freeze-manifest.json", "evals/wor105/results.jsonl", "evals/wor108/migration-impact.json", "references/assets/orchestration/workflow.md", "references/evals/development/evals.json", "references/evals/orchestration/evals.json", "tests/test_orchestration_reviews.py", "tests/test_orchestration_skill_rule_boundary.py", "tests/test_orchestration_workflow_contracts.py", "tests/test_wor105_native_transition.py", "tests/test_wor108_context_projection.py"], "evaluations": []}, "deleted": {"skills": [], "rules": [], "contracts": [], "runtime_operations": [], "artifact_semantics": [], "evaluations": []} }, "semantic_deltas": { diff --git a/tests/test_wor105_native_transition.py b/tests/test_wor105_native_transition.py index 7948800..4ec49fb 100644 --- a/tests/test_wor105_native_transition.py +++ b/tests/test_wor105_native_transition.py @@ -33,6 +33,7 @@ "review_sha256", "accepted_commit", "accepted_tree", + "release_anchor", "integrated_validation", "handoff_validation", "participant_handoffs", @@ -62,6 +63,12 @@ def _validate_transition_record(transition: dict[str, object]) -> None: assert re.fullmatch(r"[0-9a-f]{64}", str(transition["review_sha256"])) assert re.fullmatch(r"[0-9a-f]{40}", str(transition["accepted_commit"])) assert re.fullmatch(r"[0-9a-f]{40}", str(transition["accepted_tree"])) + release_anchor = transition["release_anchor"] + assert isinstance(release_anchor, dict) + assert release_anchor == { + "commit": "cfa089f0d2ed211b98d049eb37bfcdccb8091516", + "tree": "12e4a696c3caf991654f0b9ac9ef40594699c8d4", + } assert re.fullmatch( rf"{transition['accepted_commit']}\+repository-evidence-sha256:[0-9a-f]{{64}}", str(transition["source_identity"]), @@ -100,6 +107,7 @@ def test_repository_frozen_transition_record_is_self_validating() -> None: lambda value: value.update(review_sha256="not-a-digest"), lambda value: value["excluded_work"].append("unapproved-work"), lambda value: value.update(source_identity="substitute-identity"), + lambda value: value["release_anchor"].update(commit="substitute-identity"), ], ) def test_frozen_transition_validation_rejects_incomplete_or_injected_records(mutation) -> None: From ea480e7e36f3d7b849735d4ae84b00daee2ca50d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 13:12:54 +0800 Subject: [PATCH 17/48] test(orchestration): converge WOR-109 closure counts --- evals/wor109/migration-impact.json | 2 +- tests/test_wor108_closure.py | 2 +- tests/test_wor109_closure.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/evals/wor109/migration-impact.json b/evals/wor109/migration-impact.json index beb7bde..3c6f82d 100644 --- a/evals/wor109/migration-impact.json +++ b/evals/wor109/migration-impact.json @@ -5,7 +5,7 @@ "accepted_worktree": {"commit": "3cf749bbb34b208ca852a5b77bcf26645a50d4cb", "tree": "e343bf27f41996bd8189949ae6749f48abc09652", "status": "clean"}, "changed_surfaces": { "added": {"skills": [], "rules": [], "contracts": [], "runtime_operations": [], "artifact_semantics": [], "evaluations": ["evals/wor109/contracts-v1.schema.json", "evals/wor109/fixtures.json", "evals/wor109/migration-impact.json", "evals/wor109/verify.py", "tests/test_wor109_accepted_result.py", "tests/test_wor109_closure.py", "tests/test_wor109_lifecycle.py", "tests/test_wor109_lightweight.py", "tests/test_wor109_planner_contracts.py", "tests/test_wor109_planner_runtime.py"]}, - "modified": {"skills": ["skills/dev-create-task-plan/SKILL.md", "skills/orch-create-implementation-plan/SKILL.md", "skills/orch-create-specification/SKILL.md"], "rules": ["rules/orchestration/orch-artifact-authoring.md"], "contracts": ["references/assets/orchestration/contract/plan-v1.md", "references/assets/orchestration/contract/stage-event-v1.schema.json"], "runtime_operations": ["scripts/orchestration/dispatcher.py", "scripts/orchestration/execution_context.py", "scripts/orchestration/plans.py", "scripts/orchestration/review_runtime.py", "scripts/orchestration/task_ownership.py", "scripts/work-bundle/stage_events.py"], "artifact_semantics": ["evals/wor105/components/native-transition-record.yaml", "evals/wor105/freeze-manifest.json", "evals/wor105/results.jsonl", "evals/wor108/migration-impact.json", "references/assets/orchestration/workflow.md", "references/evals/development/evals.json", "references/evals/orchestration/evals.json", "tests/test_orchestration_reviews.py", "tests/test_orchestration_skill_rule_boundary.py", "tests/test_orchestration_workflow_contracts.py", "tests/test_wor105_native_transition.py", "tests/test_wor108_context_projection.py"], "evaluations": []}, + "modified": {"skills": ["skills/dev-create-task-plan/SKILL.md", "skills/orch-create-implementation-plan/SKILL.md", "skills/orch-create-specification/SKILL.md"], "rules": ["rules/orchestration/orch-artifact-authoring.md"], "contracts": ["references/assets/orchestration/contract/plan-v1.md", "references/assets/orchestration/contract/stage-event-v1.schema.json"], "runtime_operations": ["scripts/orchestration/dispatcher.py", "scripts/orchestration/execution_context.py", "scripts/orchestration/plans.py", "scripts/orchestration/review_runtime.py", "scripts/orchestration/task_ownership.py", "scripts/work-bundle/stage_events.py"], "artifact_semantics": ["evals/wor105/components/native-transition-record.yaml", "evals/wor105/freeze-manifest.json", "evals/wor105/results.jsonl", "evals/wor108/migration-impact.json", "references/assets/orchestration/workflow.md", "references/evals/development/evals.json", "references/evals/orchestration/evals.json", "tests/test_orchestration_reviews.py", "tests/test_orchestration_skill_rule_boundary.py", "tests/test_orchestration_workflow_contracts.py", "tests/test_wor105_native_transition.py", "tests/test_wor108_closure.py", "tests/test_wor108_context_projection.py"], "evaluations": []}, "deleted": {"skills": [], "rules": [], "contracts": [], "runtime_operations": [], "artifact_semantics": [], "evaluations": []} }, "semantic_deltas": { diff --git a/tests/test_wor108_closure.py b/tests/test_wor108_closure.py index 4eba67a..8d9bc17 100644 --- a/tests/test_wor108_closure.py +++ b/tests/test_wor108_closure.py @@ -21,7 +21,7 @@ def test_wor108_closure_registry_has_exact_public_fixture_identities() -> None: assert result == { "evaluation_id": "wor108-legacy-closure-v1", "fixtures": 22, - "changed_surfaces": 44, + "changed_surfaces": 63, "verdict": "accepted", } diff --git a/tests/test_wor109_closure.py b/tests/test_wor109_closure.py index adcccf8..685c036 100644 --- a/tests/test_wor109_closure.py +++ b/tests/test_wor109_closure.py @@ -15,7 +15,7 @@ spec.loader.exec_module(verifier) def test_wor109_closure_has_exact_fixture_and_manifest_identity() -> None: - assert verifier.verify() == {"evaluation_id": "wor109-review-stable-orchestration-v1", "fixtures": 14, "changed_surfaces": 31, "verdict": "accepted"} + assert verifier.verify() == {"evaluation_id": "wor109-review-stable-orchestration-v1", "fixtures": 14, "changed_surfaces": 35, "verdict": "accepted"} def test_wor109_fixture_registry_is_closed_and_unique() -> None: payload = json.loads((EVAL / "fixtures.json").read_text(encoding="utf-8")) From 8e45379359d6a2bec2a287115836f2e10438e97b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 17:22:35 +0800 Subject: [PATCH 18/48] test(wor-109): bind PD semantic scenario oracles --- evals/wor109/fixtures.json | 28 +-- evals/wor109/verify.py | 6 + tests/test_wor109_closure.py | 13 ++ tests/test_wor109_planner_scenarios.py | 292 +++++++++++++++++++++++++ 4 files changed, 325 insertions(+), 14 deletions(-) create mode 100644 tests/test_wor109_planner_scenarios.py diff --git a/evals/wor109/fixtures.json b/evals/wor109/fixtures.json index a63575b..1526769 100644 --- a/evals/wor109/fixtures.json +++ b/evals/wor109/fixtures.json @@ -2,19 +2,19 @@ "contract": "wor109-closure-fixtures-v1", "evaluation_id": "wor109-review-stable-orchestration-v1", "fixtures": [ - {"id":"PD-01","scenario":"coarse plan versus larger plan with evidenced independent runtime entry points","oracle":"larger plan allowed only for independently repairable seams; cardinality has no reward","source_ids":["REQ-PD-001","REQ-PD-004"],"pytest_node":"tests/test_wor109_planner_contracts.py::test_heavy_planning_decomposes_from_production_and_repair_seams"}, - {"id":"PD-02","scenario":"task owns a review/runtime helper but omits its known production lifecycle surface","oracle":"planner contracts require an authoritative production owner","source_ids":["REQ-PD-001","REQ-PD-002"],"pytest_node":"tests/test_wor109_accepted_result.py::test_shared_scope_canonicalizer_normalizes_equivalent_paths_and_rejects_unsafe"}, - {"id":"PD-03","scenario":"scheduler/ownership helper and unit fixture exist but executor acceptance path is unassigned","oracle":"production acceptance authority is explicitly bound","source_ids":["REQ-PD-002"],"pytest_node":"tests/test_wor109_accepted_result.py::test_accepted_result_is_deterministic_current_authority_not_handoff_history"}, - {"id":"PD-04","scenario":"two production entry points implement one requirement but fail and repair independently","oracle":"independent repair identities remain distinct","source_ids":["REQ-PD-001","REQ-PD-005"],"pytest_node":"tests/test_wor109_accepted_result.py::test_actual_accepted_repair_review_mode_and_frontier_are_digest_authority"}, - {"id":"PD-05","scenario":"single-file mechanical change has one scope, oracle, owner, and repair frontier","oracle":"coherent mechanical increments are not micro-tasked","source_ids":["REQ-PD-004","REQ-PD-005"],"pytest_node":"tests/test_wor109_lifecycle.py::test_current_accepted_result_does_not_read_handoff_or_replay_validation"}, - {"id":"PD-06","scenario":"two independent producers feed one convergence consumer","oracle":"actual barriers and convergence are represented","source_ids":["REQ-PD-005"],"pytest_node":"tests/test_wor109_planner_runtime.py::test_pd_08_stage_event_path_isolates_same_process_different_plan_economics"}, - {"id":"PD-07","scenario":"load-bearing telemetry/oracle field is omitted solely for brevity","oracle":"complete nonredundant authority retains load-bearing fields","source_ids":["REQ-SPEC-001"],"pytest_node":"tests/test_wor109_lifecycle.py::test_task_completion_reuses_current_accepted_result_without_handoff"}, - {"id":"PD-08","scenario":"one plan proceeds through reviews, scope repairs, task repairs, reruns, and final acceptance","oracle":"planning economics are emitted without cardinality judgment","source_ids":["REQ-PD-006"],"pytest_node":"tests/test_wor109_planner_runtime.py::test_pd_08_rejects_caller_injected_economics"}, - {"id":"PD-09","scenario":"review proves a coherent task needs a separately owned production path or validation boundary","oracle":"affected region returns to plan owner while unaffected evidence is preserved","source_ids":["REQ-PD-003","CON-003"],"pytest_node":"tests/test_wor109_planner_runtime.py::test_pd_07_returns_only_affected_region_and_preserves_unaffected_evidence"}, - {"id":"PD-10","scenario":"tasks are proposed only for hypothetical future defects without a current seam","oracle":"speculative fragments are not added","source_ids":["REQ-PD-004"],"pytest_node":"tests/test_wor109_lightweight.py::test_lightweight_plan_escalates_material_under_decomposition"}, - {"id":"PD-11","scenario":"lightweight task discovers one same-owner file with unchanged authority and validation","oracle":"one exact pre-mutation amendment remains lightweight","source_ids":["REQ-LW-001","REQ-LW-003"],"pytest_node":"tests/test_wor109_lightweight.py::test_lightweight_plan_allows_one_bounded_pre_mutation_path_amendment"}, - {"id":"PD-12","scenario":"lightweight work discovers a new owner, boundary, wide impact, API decision, repository, or barrier","oracle":"material scope pressure escalates to full orchestration","source_ids":["REQ-LW-002"],"pytest_node":"tests/test_wor109_lifecycle.py::test_dependency_and_phase_gates_consume_only_current_accepted_results"}, - {"id":"PD-13","scenario":"normal eligible lightweight change runs after WOR-109","oracle":"one disposable plan remains isolated from heavy machinery","source_ids":["REQ-LW-003"],"pytest_node":"tests/test_wor109_lightweight.py::test_lightweight_scope_pressure_evals_cover_wor109_boundaries"}, - {"id":"PD-14","scenario":"equivalent under-decomposition evidence reaches heavy and lightweight lanes","oracle":"heavy reslices and lightweight escalates without silent widening","source_ids":["REQ-PD-003","REQ-LW-002"],"pytest_node":"tests/test_wor109_planner_runtime.py::test_pd_07_rejects_unbounded_or_ambiguous_affected_regions"} + {"id":"PD-01","scenario":"coarse plan versus larger plan with evidenced independent runtime entry points","oracle":"larger plan allowed only for independently repairable seams; cardinality has no reward","source_ids":["REQ-PD-001","REQ-PD-004"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_01_cardinality_never_overrides_evidenced_runtime_seams"}, + {"id":"PD-02","scenario":"task owns a review/runtime helper but omits its known production lifecycle surface","oracle":"planner contracts require an authoritative production owner","source_ids":["REQ-PD-001","REQ-PD-002"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_02_helper_allocation_cannot_leave_production_lifecycle_unowned"}, + {"id":"PD-03","scenario":"scheduler/ownership helper and unit fixture exist but executor acceptance path is unassigned","oracle":"production acceptance authority is explicitly bound","source_ids":["REQ-PD-002"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_03_executor_acceptance_path_has_explicit_controller_authority"}, + {"id":"PD-04","scenario":"two production entry points implement one requirement but fail and repair independently","oracle":"independent repair identities remain distinct","source_ids":["REQ-PD-001","REQ-PD-005"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_04_independently_repairable_entry_points_remain_distinct"}, + {"id":"PD-05","scenario":"single-file mechanical change has one scope, oracle, owner, and repair frontier","oracle":"coherent mechanical increments are not micro-tasked","source_ids":["REQ-PD-004","REQ-PD-005"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_05_coherent_mechanical_increment_is_not_micro_tasked_by_file_count"}, + {"id":"PD-06","scenario":"two independent producers feed one convergence consumer","oracle":"actual barriers and convergence are represented","source_ids":["REQ-PD-005"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_06_producer_convergence_requires_a_real_barrier_and_owner"}, + {"id":"PD-07","scenario":"load-bearing telemetry/oracle field is omitted solely for brevity","oracle":"complete nonredundant authority retains load-bearing fields","source_ids":["REQ-SPEC-001"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_07_load_bearing_specification_authority_survives_compaction"}, + {"id":"PD-08","scenario":"one plan proceeds through reviews, scope repairs, task repairs, reruns, and final acceptance","oracle":"planning economics are emitted without cardinality judgment","source_ids":["REQ-PD-006"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_08_planning_economics_are_derived_without_cardinality_judgment"}, + {"id":"PD-09","scenario":"review proves a coherent task needs a separately owned production path or validation boundary","oracle":"affected region returns to plan owner while unaffected evidence is preserved","source_ids":["REQ-PD-003","CON-003"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_09_under_decomposition_returns_only_the_affected_plan_region"}, + {"id":"PD-10","scenario":"tasks are proposed only for hypothetical future defects without a current seam","oracle":"speculative fragments are not added","source_ids":["REQ-PD-004"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_10_hypothetical_defects_do_not_create_speculative_tasks"}, + {"id":"PD-11","scenario":"lightweight task discovers one same-owner file with unchanged authority and validation","oracle":"one exact pre-mutation amendment remains lightweight","source_ids":["REQ-LW-001","REQ-LW-003"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_11_same_owner_pre_mutation_path_amendment_stays_lightweight"}, + {"id":"PD-12","scenario":"lightweight work discovers a new owner, boundary, wide impact, API decision, repository, or barrier","oracle":"material scope pressure escalates to full orchestration","source_ids":["REQ-LW-002"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_12_material_lightweight_scope_pressure_escalates"}, + {"id":"PD-13","scenario":"normal eligible lightweight change runs after WOR-109","oracle":"one disposable plan remains isolated from heavy machinery","source_ids":["REQ-LW-003"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_13_normal_lightweight_change_remains_one_disposable_plan"}, + {"id":"PD-14","scenario":"equivalent under-decomposition evidence reaches heavy and lightweight lanes","oracle":"heavy reslices and lightweight escalates without silent widening","source_ids":["REQ-PD-003","REQ-LW-002"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_14_equivalent_under_decomposition_routes_by_lane_without_widening"} ] } diff --git a/evals/wor109/verify.py b/evals/wor109/verify.py index 890fc55..6403ab9 100644 --- a/evals/wor109/verify.py +++ b/evals/wor109/verify.py @@ -54,6 +54,12 @@ def _verify_fixtures(path: Path) -> tuple[str, ...]: node = item["pytest_node"] if not NODE.fullmatch(node) or node in nodes: raise VerificationError(f"invalid or duplicate pytest node: {node}") + semantic_prefix = ( + "tests/test_wor109_planner_scenarios.py::" + f"test_pd_{item['id'][3:]}_" + ) + if not node.startswith(semantic_prefix): + raise VerificationError(f"semantic oracle binding mismatch: {item['id']}") nodes.add(node) test_path, function = node.split("::") source = REPO_ROOT / test_path diff --git a/tests/test_wor109_closure.py b/tests/test_wor109_closure.py index 685c036..1897a9e 100644 --- a/tests/test_wor109_closure.py +++ b/tests/test_wor109_closure.py @@ -30,6 +30,19 @@ def test_wor109_tampered_fixture_is_rejected(tmp_path: Path) -> None: with pytest.raises(verifier.VerificationError, match="exactly PD-01..PD-14"): verifier.verify(fixtures_path=path) + +def test_wor109_valid_existing_node_semantic_substitution_is_rejected(tmp_path: Path) -> None: + payload = json.loads((EVAL / "fixtures.json").read_text(encoding="utf-8")) + first = payload["fixtures"][0] + second = payload["fixtures"][1] + first["pytest_node"], second["pytest_node"] = second["pytest_node"], first["pytest_node"] + path = tmp_path / "fixtures.json" + path.write_text(json.dumps(payload), encoding="utf-8") + + with pytest.raises(verifier.VerificationError, match="semantic oracle binding mismatch"): + verifier.verify(fixtures_path=path) + + def test_wor109_tampered_manifest_is_rejected(tmp_path: Path) -> None: payload = copy.deepcopy(json.loads((EVAL / "migration-impact.json").read_text(encoding="utf-8"))) payload["exclusions"]["WOR-107"]["authorized"] = True diff --git a/tests/test_wor109_planner_scenarios.py b/tests/test_wor109_planner_scenarios.py new file mode 100644 index 0000000..f45ad91 --- /dev/null +++ b/tests/test_wor109_planner_scenarios.py @@ -0,0 +1,292 @@ +from __future__ import annotations + +import sys +from copy import deepcopy +from pathlib import Path + +import pytest + + +ROOT = Path(__file__).resolve().parents[1] +ORCHESTRATION = ROOT / "scripts/orchestration" +WORK_BUNDLE = ROOT / "scripts/work-bundle" +sys.path.insert(0, str(ORCHESTRATION)) + +from review_runtime import route_review_verdict # noqa: E402 +import execution_context # noqa: E402 +from test_wor109_accepted_result import _binding, _handoff, _task, _validated # noqa: E402 + +sys.path.insert(0, str(WORK_BUNDLE)) +from stage_events import derive_planning_economics, validate_stage_event # noqa: E402 + + +ZERO_SHA = "0" * 64 +ZERO_TREE = "0" * 40 + + +def read(path: str) -> str: + return (ROOT / path).read_text(encoding="utf-8") + + +def planning_contracts() -> tuple[str, ...]: + return ( + read("skills/orch-create-implementation-plan/SKILL.md"), + read("rules/orchestration/orch-artifact-authoring.md"), + read("references/assets/orchestration/contract/plan-v1.md"), + read("references/assets/orchestration/workflow.md"), + ) + + +def stage_event( + event_id: str, + *, + stage: str = "implementation", + event_type: str = "stage_started", + task_id: str | None = None, + review_id: str | None = None, +) -> object: + return validate_stage_event( + { + "event_id": event_id, + "timestamp": "2026-09-07T00:00:00Z", + "process_id": "process-001", + "stage": stage, + "attempt_id": event_id, + "event_type": event_type, + "enforcement_mode": "native", + "join_ids": { + "specification_id": "spec-001", + "plan_id": "plan-001", + "phase_id": "phase-001", + "task_id": task_id, + "review_id": review_id, + "evaluation_id": None, + }, + "clocks": {"wall_ms": 1, "active_ms": 1, "billed_ms": None}, + "finding_class": None, + "return_reason": None, + "owner": "plan_owner", + "identity": { + "product_tree": ZERO_TREE, + "artifact_digest": ZERO_SHA, + "mutation_epoch": 1, + }, + "privacy": "operational_metadata_only", + } + ) + + +def allocation_gap() -> dict[str, object]: + return { + "finding_id": "finding-under-decomposed", + "stage": "implementation", + "class": "allocation_gap", + "severity": "blocking", + "first_broken_artifact": "plan", + "obligation_basis": "accepted_requirement", + "evidence": [ + { + "kind": "runtime", + "locator": "task-003", + "digest_or_identity": "independent-repair-frontiers", + "observation": "Two independently owned regions now fail separately.", + } + ], + "target_identity": { + "artifact_id": "plan-001", + "revision": "1", + "sha256": ZERO_SHA, + "source_tree": ZERO_TREE, + }, + "summary": "The task is materially under-decomposed.", + "recommended_owner": "plan_owner", + "disposition": "reslice_plan", + } + + +def test_pd_01_cardinality_never_overrides_evidenced_runtime_seams() -> None: + for contract in planning_contracts(): + assert "task or phase cardinality" in contract + assert "expected total orchestration cost" in contract + assert "production, dependency, validation, review, and repair seams" in contract + + +def test_pd_02_helper_allocation_cannot_leave_production_lifecycle_unowned() -> None: + for contract in planning_contracts(): + assert "authoritative production path" in contract + assert "production owner" in contract + assert "helper-only" in contract + + +def test_pd_03_executor_acceptance_path_has_explicit_controller_authority( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + execution = read("skills/orch-execute-plan/SKILL.md") + workflow = read("references/assets/orchestration/workflow.md") + assert "TaskOwnershipScheduler.validate_acceptance" in execution + assert "validate the executor-result with the shared helper" in execution + assert "Schedulers own dependencies, barriers, context compilation" in workflow + assert "they do not perform code-quality review or mutate task write scope" in workflow + + task = _task(tmp_path) + binding = _binding(tmp_path) + monkeypatch.setattr( + execution_context, + "capture_repository_evidence", + lambda _root: { + "head": "a" * 40, + "tree": "b" * 40, + "entries": {}, + "status": "clean", + }, + ) + first = execution_context.build_accepted_task_result( + task, binding, _handoff(), _validated(), accepted_at="2026-09-07T00:00:00Z" + ) + historical = deepcopy(binding) + historical["ownership"]["history"].append({"event": "historical-audit"}) + second = execution_context.build_accepted_task_result( + task, historical, _handoff(), _validated(), accepted_at="2026-09-07T00:00:00Z" + ) + assert first == second + assert "mutation_events" not in repr(first) + + +def test_pd_04_independently_repairable_entry_points_remain_distinct() -> None: + planner = read("skills/orch-create-implementation-plan/SKILL.md") + assert "Split independently owned entry points only when current repository evidence proves distinct ownership or repair seams" in planner + assert "bounded failure radius" in planner + + +def test_pd_05_coherent_mechanical_increment_is_not_micro_tasked_by_file_count() -> None: + for contract in planning_contracts(): + assert "coherent mechanical increment" in contract + assert "one owner, oracle, and repair frontier" in contract + + +def test_pd_06_producer_convergence_requires_a_real_barrier_and_owner() -> None: + planner = read("skills/orch-create-implementation-plan/SKILL.md") + plan_contract = read("references/assets/orchestration/contract/plan-v1.md") + assert "actual barrier or convergence boundary" in planner + assert "explicit barrier ID, readiness evidence, and convergence owner" in planner + assert "actual barrier or convergence boundary" in plan_contract + + +def test_pd_07_load_bearing_specification_authority_survives_compaction() -> None: + specification = read("skills/orch-create-specification/SKILL.md") + assert "complete, nonredundant authoritative specification" in specification + assert "Preserve every load-bearing requirement, constraint, interface, acceptance criterion, validation target, and decision" in specification + assert "Reject duplicate prose that adds no authority" in specification + + +def test_pd_08_planning_economics_are_derived_without_cardinality_judgment() -> None: + records = [ + stage_event("phase"), + stage_event("task-a", task_id="task-a"), + stage_event("task-b", task_id="task-b"), + stage_event( + "accepted", + stage="integrated_implementation", + event_type="stage_completed", + review_id="review-001", + ), + ] + result = derive_planning_economics(records, process_id="process-001", plan_id="plan-001") + assert result["initial_cardinality"] == {"phases": 1, "tasks": 2} + assert result["plan_revisions"] == 0 + assert set(result) == { + "initial_cardinality", + "plan_revisions", + "plan_reviews", + "scope_allocation_repairs", + "task_review_repairs", + "validation_reruns", + "first_green_to_final_accept_ms", + } + + +def test_pd_09_under_decomposition_returns_only_the_affected_plan_region() -> None: + binding = {"binding_id": "binding-task-003", "sha256": "1" * 64} + baseline = {"head": "2" * 40, "tree": "3" * 40} + unaffected = [ + { + "artifact_id": "task-001", + "revision": "1", + "sha256": ZERO_SHA, + "source_tree": ZERO_TREE, + } + ] + region = { + "task_ids": ["task-003"], + "paths": ["scripts/orchestration/review_runtime.py"], + "interfaces": ["API-PD-001"], + "validation_oracles": ["VAL-004"], + } + routed = route_review_verdict( + allocation_gap(), + affected_region=region, + unaffected_evidence_identities=unaffected, + original_binding_identity=binding, + original_baseline_identity=baseline, + ) + assert routed["execution_state"] == "paused_for_reslice" + assert routed["affected_region"] == region + assert routed["preserved_evidence_identities"] == unaffected + assert routed["silent_expansion_allowed"] is False + + +def test_pd_10_hypothetical_defects_do_not_create_speculative_tasks() -> None: + for contract in planning_contracts(): + assert "speculative" in contract + assert "dependency, ownership, validation" in contract + assert "Do not create speculative splits unsupported by current authority" in planning_contracts()[0] + + +def test_pd_11_same_owner_pre_mutation_path_amendment_stays_lightweight() -> None: + lightweight = read("skills/dev-create-task-plan/SKILL.md") + assert "Before the first write" in lightweight + assert "exactly one additional path" in lightweight + assert "same implementation owner" in lightweight + assert "materially unchanged" in lightweight + + +def test_pd_12_material_lightweight_scope_pressure_escalates() -> None: + lightweight = read("skills/dev-create-task-plan/SKILL.md") + for boundary in ( + "new production or lifecycle owner", + "independent validation boundary", + "wide impact", + "API or workflow decision", + "second repository", + "barrier or convergence topology", + ): + assert boundary in lightweight + assert "stop and escalate to full orchestration" in lightweight + + +def test_pd_13_normal_lightweight_change_remains_one_disposable_plan() -> None: + lightweight = read("skills/dev-create-task-plan/SKILL.md") + assert "Keep one disposable `.work-bundle/runtime/dev-plans/` artifact" in lightweight + for forbidden_import in ("executor-result", "`Completed`", "review package", "archive helper"): + assert forbidden_import in lightweight + assert "Do not import" in lightweight + + +def test_pd_14_equivalent_under_decomposition_routes_by_lane_without_widening() -> None: + routed = route_review_verdict( + allocation_gap(), + affected_region={ + "task_ids": ["task-003"], + "paths": ["scripts/orchestration/review_runtime.py"], + "interfaces": ["API-PD-001"], + "validation_oracles": ["VAL-004"], + }, + original_binding_identity={"binding_id": "binding-task-003", "sha256": "1" * 64}, + original_baseline_identity={"head": "2" * 40, "tree": "3" * 40}, + ) + lightweight = read("skills/dev-create-task-plan/SKILL.md") + assert routed["action"] == "reslice_plan" + assert routed["silent_expansion_allowed"] is False + assert "materially under-decomposed" in lightweight + assert "stop and escalate to full orchestration" in lightweight + assert "must not be repeated" in lightweight From 79326d53c5e5a8b6daa46390ec180c0b3ce18e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 17:38:39 +0800 Subject: [PATCH 19/48] test(wor-109): exercise PD planner behaviors --- tests/test_wor109_planner_scenarios.py | 383 ++++++++++++++++++++----- 1 file changed, 311 insertions(+), 72 deletions(-) diff --git a/tests/test_wor109_planner_scenarios.py b/tests/test_wor109_planner_scenarios.py index f45ad91..6b89700 100644 --- a/tests/test_wor109_planner_scenarios.py +++ b/tests/test_wor109_planner_scenarios.py @@ -1,10 +1,12 @@ from __future__ import annotations +import json import sys from copy import deepcopy from pathlib import Path import pytest +from reviewer_run_fixtures import bind_review_receipt ROOT = Path(__file__).resolve().parents[1] @@ -12,9 +14,20 @@ WORK_BUNDLE = ROOT / "scripts/work-bundle" sys.path.insert(0, str(ORCHESTRATION)) -from review_runtime import route_review_verdict # noqa: E402 +from review_runtime import ( # noqa: E402 + ReviewContractError, + plan_review_identity, + resume_plan_return, + route_review_verdict, +) import execution_context # noqa: E402 from test_wor109_accepted_result import _binding, _handoff, _task, _validated # noqa: E402 +from task_ownership import ( # noqa: E402 + OwnershipBlocker, + SubagentDispatch, + TaskCandidate, + TaskOwnershipScheduler, +) sys.path.insert(0, str(WORK_BUNDLE)) from stage_events import derive_planning_economics, validate_stage_event # noqa: E402 @@ -28,15 +41,6 @@ def read(path: str) -> str: return (ROOT / path).read_text(encoding="utf-8") -def planning_contracts() -> tuple[str, ...]: - return ( - read("skills/orch-create-implementation-plan/SKILL.md"), - read("rules/orchestration/orch-artifact-authoring.md"), - read("references/assets/orchestration/contract/plan-v1.md"), - read("references/assets/orchestration/workflow.md"), - ) - - def stage_event( event_id: str, *, @@ -44,14 +48,18 @@ def stage_event( event_type: str = "stage_started", task_id: str | None = None, review_id: str | None = None, + timestamp: str = "2026-09-07T00:00:00Z", + attempt_id: str | None = None, + finding_class: str | None = None, + evaluation_id: str | None = None, ) -> object: return validate_stage_event( { "event_id": event_id, - "timestamp": "2026-09-07T00:00:00Z", + "timestamp": timestamp, "process_id": "process-001", "stage": stage, - "attempt_id": event_id, + "attempt_id": attempt_id or event_id, "event_type": event_type, "enforcement_mode": "native", "join_ids": { @@ -60,10 +68,10 @@ def stage_event( "phase_id": "phase-001", "task_id": task_id, "review_id": review_id, - "evaluation_id": None, + "evaluation_id": evaluation_id, }, "clocks": {"wall_ms": 1, "active_ms": 1, "billed_ms": None}, - "finding_class": None, + "finding_class": finding_class, "return_reason": None, "owner": "plan_owner", "identity": { @@ -76,6 +84,57 @@ def stage_event( ) +class AvailableAdapter: + def available(self) -> bool: + return True + + def dispatch(self, task: TaskCandidate, *, operation: str) -> SubagentDispatch: + return SubagentDispatch( + task.task_id, + { + "delegated": True, + "owner_kind": "subagent", + "agent_id": f"agent-{task.task_id}", + "run_id": f"run-{task.task_id}", + "mechanism": "host-native", + }, + ) + + def wait(self, handle: object) -> object: + return {"completed": handle} + + +class UnavailableAdapter(AvailableAdapter): + def available(self) -> bool: + return False + + +def candidate( + task_id: str, + *paths: str, + dependencies: tuple[str, ...] = (), + common_contract: str | None = None, + barrier: str | None = None, + convergence_owner: str | None = None, + barrier_participants: tuple[str, ...] = (), +) -> TaskCandidate: + return TaskCandidate( + task_id=task_id, + dependencies=dependencies, + write_scope=paths, + execution_workspace=f"workspace-{task_id}", + common_contract=common_contract, + barrier=barrier, + convergence_owner=convergence_owner, + barrier_participants=barrier_participants, + ) + + +def development_case(case_id: str) -> dict[str, object]: + payload = json.loads(read("references/evals/development/evals.json")) + return next(case for case in payload["evals"] if case["id"] == case_id) + + def allocation_gap() -> dict[str, object]: return { "finding_id": "finding-under-decomposed", @@ -105,28 +164,48 @@ def allocation_gap() -> dict[str, object]: def test_pd_01_cardinality_never_overrides_evidenced_runtime_seams() -> None: - for contract in planning_contracts(): - assert "task or phase cardinality" in contract - assert "expected total orchestration cost" in contract - assert "production, dependency, validation, review, and repair seams" in contract + tasks = [candidate(f"task-{index:03d}", f"src/seam-{index}.py") for index in range(1, 7)] + result = TaskOwnershipScheduler(AvailableAdapter()).run_wave(tasks, completed=set()) + + assert result.dispatched == tuple(task.task_id for task in tasks) + assert len(result.ownership) == 6 def test_pd_02_helper_allocation_cannot_leave_production_lifecycle_unowned() -> None: - for contract in planning_contracts(): - assert "authoritative production path" in contract - assert "production owner" in contract - assert "helper-only" in contract + with pytest.raises(OwnershipBlocker, match="subagent execution is unavailable"): + TaskOwnershipScheduler(UnavailableAdapter()).run_wave( + [candidate("task-production", "src/production.py")], completed=set() + ) + + scheduler = TaskOwnershipScheduler(AvailableAdapter()) + with pytest.raises(OwnershipBlocker, match="controller mutated task-owned implementation scope"): + scheduler.validate_acceptance( + delegation_evidence={ + "delegated": True, + "owner_kind": "subagent", + "agent_id": "helper-owner", + "run_id": "helper-run", + "mechanism": "host-native", + }, + mutation_events=[{"actor_kind": "controller", "paths": ["src/production.py"]}], + write_scope=["src/production.py"], + validations_passed=True, + ) def test_pd_03_executor_acceptance_path_has_explicit_controller_authority( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: execution = read("skills/orch-execute-plan/SKILL.md") - workflow = read("references/assets/orchestration/workflow.md") assert "TaskOwnershipScheduler.validate_acceptance" in execution - assert "validate the executor-result with the shared helper" in execution - assert "Schedulers own dependencies, barriers, context compilation" in workflow - assert "they do not perform code-quality review or mutate task write scope" in workflow + + with pytest.raises(OwnershipBlocker, match="requires subagent ownership"): + TaskOwnershipScheduler(AvailableAdapter()).validate_acceptance( + delegation_evidence=None, + mutation_events=[], + write_scope=["src/production.py"], + validations_passed=True, + ) task = _task(tmp_path) binding = _binding(tmp_path) @@ -153,30 +232,68 @@ def test_pd_03_executor_acceptance_path_has_explicit_controller_authority( def test_pd_04_independently_repairable_entry_points_remain_distinct() -> None: - planner = read("skills/orch-create-implementation-plan/SKILL.md") - assert "Split independently owned entry points only when current repository evidence proves distinct ownership or repair seams" in planner - assert "bounded failure radius" in planner + tasks = [ + candidate("task-entry-a", "src/entry_a.py"), + candidate("task-entry-b", "src/entry_b.py"), + ] + result = TaskOwnershipScheduler(AvailableAdapter()).run_wave(tasks, completed=set()) + assert result.dispatched == ("task-entry-a", "task-entry-b") + assert tuple(owner["agent_id"] for owner in result.ownership) == ( + "agent-task-entry-a", + "agent-task-entry-b", + ) def test_pd_05_coherent_mechanical_increment_is_not_micro_tasked_by_file_count() -> None: - for contract in planning_contracts(): - assert "coherent mechanical increment" in contract - assert "one owner, oracle, and repair frontier" in contract + task = candidate("task-mechanical", "src/edit.py", "tests/test_edit.py", "docs/edit.md") + result = TaskOwnershipScheduler(AvailableAdapter()).run_wave([task], completed=set()) + assert result.dispatched == ("task-mechanical",) + assert len(result.results) == 1 def test_pd_06_producer_convergence_requires_a_real_barrier_and_owner() -> None: - planner = read("skills/orch-create-implementation-plan/SKILL.md") - plan_contract = read("references/assets/orchestration/contract/plan-v1.md") - assert "actual barrier or convergence boundary" in planner - assert "explicit barrier ID, readiness evidence, and convergence owner" in planner - assert "actual barrier or convergence boundary" in plan_contract + scheduler = TaskOwnershipScheduler(AvailableAdapter()) + convergence = candidate( + "task-converge", + "src/converge.py", + dependencies=("task-a", "task-b"), + common_contract="contract-v1", + barrier="barrier-producers", + convergence_owner="task-converge", + barrier_participants=("task-a", "task-b"), + ) + waiting = scheduler.run_wave( + [convergence], completed={"task-a", "task-b"}, accepted_handoffs=set() + ) + released = scheduler.run_wave( + [convergence], + completed={"task-a", "task-b"}, + accepted_handoffs={"task-a", "task-b"}, + ) + assert waiting.dispatched == () + assert released.dispatched == ("task-converge",) def test_pd_07_load_bearing_specification_authority_survives_compaction() -> None: - specification = read("skills/orch-create-specification/SKILL.md") - assert "complete, nonredundant authoritative specification" in specification - assert "Preserve every load-bearing requirement, constraint, interface, acceptance criterion, validation target, and decision" in specification - assert "Reject duplicate prose that adds no authority" in specification + task = _task(Path("/tmp/wor109-authority")) + projection = execution_context._accepted_task_projection(task) + scopes = execution_context._canonical_task_scopes(task) + validation = execution_context._accepted_validation_projection(task) + + assert projection["source_ids"] == ["REQ-001"] + assert scopes == { + "read": ["src/read.py"], + "write": ["src/a.py"], + "forbidden": ["secrets/key.txt"], + } + assert validation == [ + { + "id": "VAL-001", + "command": "pytest -q", + "boundary": "component", + "freshness": "current_task_batch", + } + ] def test_pd_08_planning_economics_are_derived_without_cardinality_judgment() -> None: @@ -184,16 +301,43 @@ def test_pd_08_planning_economics_are_derived_without_cardinality_judgment() -> stage_event("phase"), stage_event("task-a", task_id="task-a"), stage_event("task-b", task_id="task-b"), + stage_event( + "scope-repair", + event_type="reslice_recorded", + attempt_id="repair-scope", + finding_class="allocation_gap", + ), + stage_event( + "task-repair", + event_type="work_returned", + task_id="task-a", + review_id="review-task-a", + attempt_id="repair-task", + finding_class="implementation_defect", + ), + stage_event("suite-first", event_type="suite_started", evaluation_id="eval-001"), + stage_event("suite-rerun", event_type="suite_started", evaluation_id="eval-001"), + stage_event( + "green", + event_type="suite_completed", + evaluation_id="eval-001", + timestamp="2026-09-07T00:00:01Z", + ), stage_event( "accepted", stage="integrated_implementation", event_type="stage_completed", review_id="review-001", + timestamp="2026-09-07T00:00:02.200Z", ), ] result = derive_planning_economics(records, process_id="process-001", plan_id="plan-001") assert result["initial_cardinality"] == {"phases": 1, "tasks": 2} - assert result["plan_revisions"] == 0 + assert result["plan_revisions"] == 1 + assert result["scope_allocation_repairs"] == 1 + assert result["task_review_repairs"] == 1 + assert result["validation_reruns"] == 1 + assert result["first_green_to_final_accept_ms"] == 1200 assert set(result) == { "initial_cardinality", "plan_revisions", @@ -205,7 +349,9 @@ def test_pd_08_planning_economics_are_derived_without_cardinality_judgment() -> } -def test_pd_09_under_decomposition_returns_only_the_affected_plan_region() -> None: +def test_pd_09_under_decomposition_returns_only_the_affected_plan_region( + tmp_path: Path, +) -> None: binding = {"binding_id": "binding-task-003", "sha256": "1" * 64} baseline = {"head": "2" * 40, "tree": "3" * 40} unaffected = [ @@ -234,42 +380,135 @@ def test_pd_09_under_decomposition_returns_only_the_affected_plan_region() -> No assert routed["preserved_evidence_identities"] == unaffected assert routed["silent_expansion_allowed"] is False + orch = tmp_path / ".work-bundle/orchestration" + spec = orch / "spec/active/spec.md" + plan = orch / "plan/active/plan.md" + spec.parent.mkdir(parents=True) + plan.parent.mkdir(parents=True) + spec.write_text("---\nid: spec-test\nstatus: verified\n---\nAuthority\n", encoding="utf-8") + plan.write_text( + "---\nid: plan-001\nstatus: Planned\nsource_spec: [spec-test]\n---\nOriginal\n", + encoding="utf-8", + ) + plan.write_text(plan.read_text(encoding="utf-8").replace("Original", "Resliced"), encoding="utf-8") + + with pytest.raises(ReviewContractError, match="accepted repaired plan-review authority"): + resume_plan_return( + routed, + workspace_root=tmp_path, + plan_path=plan, + current_binding_identity=binding, + current_baseline_identity=baseline, + current_unaffected_evidence_identities=unaffected, + ) + + review = { + "review_id": "review-plan", + "stage": "plan", + "target_identity": plan_review_identity(tmp_path, plan), + "reviewer": { + "agent_id": "reviewer-1", + "capability": "judgment", + "authorship": "none", + "repair_participation": "none", + "decision_participation": "none", + "deliberation_participation": "none", + "context_origin": "direct_source", + }, + "evidence": { + "mode": "direct", + "capabilities": ["source inspection"], + "unavailable_evidence": [], + "commands": [], + "artifacts": [], + }, + "verdict": "accepted", + "findings": [], + "started_at": "2026-09-07T00:00:00Z", + "completed_at": "2026-09-07T00:01:00Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + review = bind_review_receipt(tmp_path, review) + reviews = orch / "reviews" + reviews.mkdir() + (reviews / "plan.json").write_text(json.dumps(review), encoding="utf-8") + + resumed = resume_plan_return( + routed, + workspace_root=tmp_path, + plan_path=plan, + current_binding_identity=binding, + current_baseline_identity=baseline, + current_unaffected_evidence_identities=unaffected, + ) + assert resumed["execution_state"] == "ready_from_repaired_authority" + assert resumed["preserved_evidence_identities"] == unaffected + def test_pd_10_hypothetical_defects_do_not_create_speculative_tasks() -> None: - for contract in planning_contracts(): - assert "speculative" in contract - assert "dependency, ownership, validation" in contract - assert "Do not create speculative splits unsupported by current authority" in planning_contracts()[0] + finding = allocation_gap() + finding.update( + { + "finding_id": "finding-advisory", + "class": "advisory_enhancement", + "severity": "advisory", + "first_broken_artifact": "implementation", + "obligation_basis": "none", + "evidence": [], + "recommended_owner": "backlog_owner", + "disposition": "record_advisory", + } + ) + routed = route_review_verdict(finding) + assert routed["return_to"] == "backlog_owner" + assert routed["action"] == "record_advisory" + assert routed["execution_state"] == "returned_for_repair" def test_pd_11_same_owner_pre_mutation_path_amendment_stays_lightweight() -> None: - lightweight = read("skills/dev-create-task-plan/SKILL.md") - assert "Before the first write" in lightweight - assert "exactly one additional path" in lightweight - assert "same implementation owner" in lightweight - assert "materially unchanged" in lightweight + case = development_case("dev-lightweight-pre-mutation-one-file-amendment") + amended = candidate("task-light", "src/original.py", "src/discovered.py") + result = TaskOwnershipScheduler(AvailableAdapter()).run_wave([amended], completed=set()) + assert "Before mutation" in case["prompt"] + assert "same implementation owner" in case["prompt"] + assert "Amends Files.Modify once" in case["expected_output"] + assert result.dispatched == ("task-light",) + assert result.ownership[0]["agent_id"] == "agent-task-light" def test_pd_12_material_lightweight_scope_pressure_escalates() -> None: - lightweight = read("skills/dev-create-task-plan/SKILL.md") - for boundary in ( - "new production or lifecycle owner", - "independent validation boundary", - "wide impact", - "API or workflow decision", - "second repository", - "barrier or convergence topology", - ): - assert boundary in lightweight - assert "stop and escalate to full orchestration" in lightweight + case = development_case("dev-lightweight-material-under-decomposition") + routed = route_review_verdict( + allocation_gap(), + affected_region={ + "task_ids": ["task-light"], + "paths": ["src/new-owner.py"], + "interfaces": [], + "validation_oracles": ["VAL-NEW"], + }, + original_binding_identity={"binding_id": "binding-light", "sha256": "1" * 64}, + original_baseline_identity={"head": "2" * 40, "tree": "3" * 40}, + ) + assert "new production owner and an independent validation boundary" in case["prompt"] + assert case["expected_output"] == ( + "Treats the task as materially under-decomposed and escalates to full orchestration " + "instead of repeatedly expanding the lightweight plan." + ) + assert routed["action"] == "reslice_plan" + assert routed["silent_expansion_allowed"] is False def test_pd_13_normal_lightweight_change_remains_one_disposable_plan() -> None: - lightweight = read("skills/dev-create-task-plan/SKILL.md") - assert "Keep one disposable `.work-bundle/runtime/dev-plans/` artifact" in lightweight - for forbidden_import in ("executor-result", "`Completed`", "review package", "archive helper"): - assert forbidden_import in lightweight - assert "Do not import" in lightweight + case = development_case("dev-lightweight-amendment-lane-separation") + result = TaskOwnershipScheduler(AvailableAdapter()).run_wave( + [candidate("task-light", "src/only.py")], completed=set() + ) + assert "executor result, task state, review package, and archive record" in case["prompt"] + assert case["expected_output"] == ( + "Allows only the exact bounded Files.Modify amendment and rejects heavy lifecycle " + "artifacts; the lightweight lane remains one disposable plan." + ) + assert result.dispatched == ("task-light",) def test_pd_14_equivalent_under_decomposition_routes_by_lane_without_widening() -> None: @@ -284,9 +523,9 @@ def test_pd_14_equivalent_under_decomposition_routes_by_lane_without_widening() original_binding_identity={"binding_id": "binding-task-003", "sha256": "1" * 64}, original_baseline_identity={"head": "2" * 40, "tree": "3" * 40}, ) - lightweight = read("skills/dev-create-task-plan/SKILL.md") assert routed["action"] == "reslice_plan" assert routed["silent_expansion_allowed"] is False - assert "materially under-decomposed" in lightweight - assert "stop and escalate to full orchestration" in lightweight - assert "must not be repeated" in lightweight + assert routed["preserve_valid_work_and_evidence"] is True + case = development_case("dev-lightweight-material-under-decomposition") + assert "escalates to full orchestration" in case["expected_output"] + assert "instead of repeatedly expanding" in case["expected_output"] From eb6c658f46f9c12fa1b9be4030c5bc7dcb9b045c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 20:47:05 +0800 Subject: [PATCH 20/48] test(wor-109): bind heavy PD authority --- tests/test_wor109_planner_scenarios.py | 261 +++++++++---------------- 1 file changed, 88 insertions(+), 173 deletions(-) diff --git a/tests/test_wor109_planner_scenarios.py b/tests/test_wor109_planner_scenarios.py index 6b89700..df2c02a 100644 --- a/tests/test_wor109_planner_scenarios.py +++ b/tests/test_wor109_planner_scenarios.py @@ -22,12 +22,6 @@ ) import execution_context # noqa: E402 from test_wor109_accepted_result import _binding, _handoff, _task, _validated # noqa: E402 -from task_ownership import ( # noqa: E402 - OwnershipBlocker, - SubagentDispatch, - TaskCandidate, - TaskOwnershipScheduler, -) sys.path.insert(0, str(WORK_BUNDLE)) from stage_events import derive_planning_economics, validate_stage_event # noqa: E402 @@ -84,55 +78,31 @@ def stage_event( ) -class AvailableAdapter: - def available(self) -> bool: - return True +def development_case(case_id: str) -> dict[str, object]: + payload = json.loads(read("references/evals/development/evals.json")) + return next(case for case in payload["evals"] if case["id"] == case_id) - def dispatch(self, task: TaskCandidate, *, operation: str) -> SubagentDispatch: - return SubagentDispatch( - task.task_id, - { - "delegated": True, - "owner_kind": "subagent", - "agent_id": f"agent-{task.task_id}", - "run_id": f"run-{task.task_id}", - "mechanism": "host-native", - }, - ) - def wait(self, handle: object) -> object: - return {"completed": handle} - - -class UnavailableAdapter(AvailableAdapter): - def available(self) -> bool: - return False - - -def candidate( - task_id: str, - *paths: str, - dependencies: tuple[str, ...] = (), - common_contract: str | None = None, - barrier: str | None = None, - convergence_owner: str | None = None, - barrier_participants: tuple[str, ...] = (), -) -> TaskCandidate: - return TaskCandidate( - task_id=task_id, - dependencies=dependencies, - write_scope=paths, - execution_workspace=f"workspace-{task_id}", - common_contract=common_contract, - barrier=barrier, - convergence_owner=convergence_owner, - barrier_participants=barrier_participants, - ) +def orchestration_case(case_id: str) -> dict[str, object]: + payload = json.loads(read("references/evals/orchestration/evals.json")) + return next(case for case in payload["evals"] if case["id"] == case_id) -def development_case(case_id: str) -> dict[str, object]: - payload = json.loads(read("references/evals/development/evals.json")) - return next(case for case in payload["evals"] if case["id"] == case_id) +def assert_normative_case( + case_id: str, + *, + prompt: str, + expected_output: str, + skill_path: str, + owning_clause: str, +) -> None: + assert orchestration_case(case_id) == { + "id": case_id, + "prompt": prompt, + "expected_output": expected_output, + "files": [], + } + assert owning_clause in read(skill_path) def allocation_gap() -> dict[str, object]: @@ -164,48 +134,35 @@ def allocation_gap() -> dict[str, object]: def test_pd_01_cardinality_never_overrides_evidenced_runtime_seams() -> None: - tasks = [candidate(f"task-{index:03d}", f"src/seam-{index}.py") for index in range(1, 7)] - result = TaskOwnershipScheduler(AvailableAdapter()).run_wave(tasks, completed=set()) - - assert result.dispatched == tuple(task.task_id for task in tasks) - assert len(result.ownership) == 6 + assert_normative_case( + "PD-01", + prompt="Plan a change whose production seams support six tasks, while a reviewer proposes a three-task target to make the plan shorter.", + expected_output="Rejects the task-count target and does not optimize task or phase cardinality; it uses the six evidenced ownership, dependency, validation, review, and repair seams when they bound expected total orchestration cost.", + skill_path="skills/orch-create-implementation-plan/SKILL.md", + owning_clause="Do not optimize task or phase cardinality. Decompose only at concrete independently owned production, dependency, validation, review, and repair seams so expected total orchestration cost remains bounded", + ) def test_pd_02_helper_allocation_cannot_leave_production_lifecycle_unowned() -> None: - with pytest.raises(OwnershipBlocker, match="subagent execution is unavailable"): - TaskOwnershipScheduler(UnavailableAdapter()).run_wave( - [candidate("task-production", "src/production.py")], completed=set() - ) - - scheduler = TaskOwnershipScheduler(AvailableAdapter()) - with pytest.raises(OwnershipBlocker, match="controller mutated task-owned implementation scope"): - scheduler.validate_acceptance( - delegation_evidence={ - "delegated": True, - "owner_kind": "subagent", - "agent_id": "helper-owner", - "run_id": "helper-run", - "mechanism": "host-native", - }, - mutation_events=[{"actor_kind": "controller", "paths": ["src/production.py"]}], - write_scope=["src/production.py"], - validations_passed=True, - ) + assert_normative_case( + "PD-02", + prompt="A plan allocates tests and a helper refactor but leaves the authoritative production path with no implementation owner.", + expected_output="Rejects helper-only allocation until every authoritative production path has a production owner and the production change, validation, and repair responsibility are explicitly allocated.", + skill_path="skills/orch-create-implementation-plan/SKILL.md", + owning_clause="Assign every authoritative production path to a production owner; reject helper-only allocation while its production path is unowned.", + ) def test_pd_03_executor_acceptance_path_has_explicit_controller_authority( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: - execution = read("skills/orch-execute-plan/SKILL.md") - assert "TaskOwnershipScheduler.validate_acceptance" in execution - - with pytest.raises(OwnershipBlocker, match="requires subagent ownership"): - TaskOwnershipScheduler(AvailableAdapter()).validate_acceptance( - delegation_evidence=None, - mutation_events=[], - write_scope=["src/production.py"], - validations_passed=True, - ) + assert_normative_case( + "PD-02", + prompt="A plan allocates tests and a helper refactor but leaves the authoritative production path with no implementation owner.", + expected_output="Rejects helper-only allocation until every authoritative production path has a production owner and the production change, validation, and repair responsibility are explicitly allocated.", + skill_path="skills/orch-create-implementation-plan/SKILL.md", + owning_clause="Assign every authoritative production path to a production owner; reject helper-only allocation while its production path is unowned.", + ) task = _task(tmp_path) binding = _binding(tmp_path) @@ -232,68 +189,43 @@ def test_pd_03_executor_acceptance_path_has_explicit_controller_authority( def test_pd_04_independently_repairable_entry_points_remain_distinct() -> None: - tasks = [ - candidate("task-entry-a", "src/entry_a.py"), - candidate("task-entry-b", "src/entry_b.py"), - ] - result = TaskOwnershipScheduler(AvailableAdapter()).run_wave(tasks, completed=set()) - assert result.dispatched == ("task-entry-a", "task-entry-b") - assert tuple(owner["agent_id"] for owner in result.ownership) == ( - "agent-task-entry-a", - "agent-task-entry-b", + assert_normative_case( + "PD-03", + prompt="A planner groups two changes that have different owners, validation oracles, and independently routable repair outcomes.", + expected_output="Splits at the evidenced ownership, oracle, and repair frontier so a failure returns to the smallest affected plan region without widening unrelated accepted work.", + skill_path="skills/orch-create-implementation-plan/SKILL.md", + owning_clause="preserving independently falsifiable increments, short evidence loops, exact dependencies, disjoint write scopes, bounded failure radius, and review boundaries", ) def test_pd_05_coherent_mechanical_increment_is_not_micro_tasked_by_file_count() -> None: - task = candidate("task-mechanical", "src/edit.py", "tests/test_edit.py", "docs/edit.md") - result = TaskOwnershipScheduler(AvailableAdapter()).run_wave([task], completed=set()) - assert result.dispatched == ("task-mechanical",) - assert len(result.results) == 1 + assert_normative_case( + "PD-04", + prompt="A planner proposes splitting one production edit, its direct contract test, and its local documentation merely because three files are involved.", + expected_output="Keeps the coherent mechanical increment together under one production owner, oracle, and repair frontier; file count is not a decomposition seam.", + skill_path="skills/orch-create-implementation-plan/SKILL.md", + owning_clause="Keep one coherent mechanical increment with one owner, oracle, and repair frontier together.", + ) def test_pd_06_producer_convergence_requires_a_real_barrier_and_owner() -> None: - scheduler = TaskOwnershipScheduler(AvailableAdapter()) - convergence = candidate( - "task-converge", - "src/converge.py", - dependencies=("task-a", "task-b"), - common_contract="contract-v1", - barrier="barrier-producers", - convergence_owner="task-converge", - barrier_participants=("task-a", "task-b"), + assert_normative_case( + "PD-05", + prompt="A plan creates a new phase for each lifecycle label even though no dependency barrier or convergence boundary separates the work.", + expected_output="Rejects lifecycle-label phases and creates a phase only for an actual barrier or convergence boundary with concrete readiness and ownership evidence.", + skill_path="skills/orch-create-implementation-plan/SKILL.md", + owning_clause="Create a phase only for an actual barrier or convergence boundary, with explicit barrier ID, readiness evidence, and convergence owner.", ) - waiting = scheduler.run_wave( - [convergence], completed={"task-a", "task-b"}, accepted_handoffs=set() - ) - released = scheduler.run_wave( - [convergence], - completed={"task-a", "task-b"}, - accepted_handoffs={"task-a", "task-b"}, - ) - assert waiting.dispatched == () - assert released.dispatched == ("task-converge",) def test_pd_07_load_bearing_specification_authority_survives_compaction() -> None: - task = _task(Path("/tmp/wor109-authority")) - projection = execution_context._accepted_task_projection(task) - scopes = execution_context._canonical_task_scopes(task) - validation = execution_context._accepted_validation_projection(task) - - assert projection["source_ids"] == ["REQ-001"] - assert scopes == { - "read": ["src/read.py"], - "write": ["src/a.py"], - "forbidden": ["secrets/key.txt"], - } - assert validation == [ - { - "id": "VAL-001", - "command": "pytest -q", - "boundary": "component", - "freshness": "current_task_batch", - } - ] + assert_normative_case( + "PD-06", + prompt="A specification is shortened by deleting a unique validation target and compatibility constraint while retaining repeated summary prose.", + expected_output="Restores a complete, nonredundant authority set: preserves every load-bearing field required downstream and removes duplicate prose rather than unique authority.", + skill_path="skills/orch-create-specification/SKILL.md", + owning_clause="Preserve every load-bearing requirement, constraint, interface, acceptance criterion, validation target, and decision needed downstream; such authority must not be removed merely to make the artifact smaller. Reject duplicate prose that adds no authority.", + ) def test_pd_08_planning_economics_are_derived_without_cardinality_judgment() -> None: @@ -352,6 +284,13 @@ def test_pd_08_planning_economics_are_derived_without_cardinality_judgment() -> def test_pd_09_under_decomposition_returns_only_the_affected_plan_region( tmp_path: Path, ) -> None: + assert_normative_case( + "PD-07", + prompt="Execution proves one task materially under-decomposed after its repair frontier separates into two independently owned regions.", + expected_output="Stops repeatedly enlarging the task, requires a return to the plan, and reslices only the affected region while preserving the original binding, baseline, accepted unaffected regions, and typed repair route.", + skill_path="skills/orch-create-implementation-plan/SKILL.md", + owning_clause="When execution proves a task materially under-decomposed, return to the plan and reslice only the affected region around the newly evidenced seam. Preserve the original binding, baseline, and accepted unaffected regions; do not repeatedly enlarge the task.", + ) binding = {"binding_id": "binding-task-003", "sha256": "1" * 64} baseline = {"head": "2" * 40, "tree": "3" * 40} unaffected = [ @@ -446,72 +385,48 @@ def test_pd_09_under_decomposition_returns_only_the_affected_plan_region( def test_pd_10_hypothetical_defects_do_not_create_speculative_tasks() -> None: - finding = allocation_gap() - finding.update( - { - "finding_id": "finding-advisory", - "class": "advisory_enhancement", - "severity": "advisory", - "first_broken_artifact": "implementation", - "obligation_basis": "none", - "evidence": [], - "recommended_owner": "backlog_owner", - "disposition": "record_advisory", - } + assert_normative_case( + "PD-09", + prompt="A planner proposes separate hardening, compatibility, and recovery tasks without current authority, repository, dependency, validation, or acceptance evidence for them.", + expected_output="Rejects speculative fragmentation and adds no tasks until a current material seam proves the scope; it does not create a second review, retry, or recovery subsystem.", + skill_path="skills/orch-create-implementation-plan/SKILL.md", + owning_clause="Do not create speculative splits unsupported by current authority, repository, dependency, ownership, validation, or acceptance evidence.", ) - routed = route_review_verdict(finding) - assert routed["return_to"] == "backlog_owner" - assert routed["action"] == "record_advisory" - assert routed["execution_state"] == "returned_for_repair" def test_pd_11_same_owner_pre_mutation_path_amendment_stays_lightweight() -> None: case = development_case("dev-lightweight-pre-mutation-one-file-amendment") - amended = candidate("task-light", "src/original.py", "src/discovered.py") - result = TaskOwnershipScheduler(AvailableAdapter()).run_wave([amended], completed=set()) assert "Before mutation" in case["prompt"] assert "same implementation owner" in case["prompt"] assert "Amends Files.Modify once" in case["expected_output"] - assert result.dispatched == ("task-light",) - assert result.ownership[0]["agent_id"] == "agent-task-light" def test_pd_12_material_lightweight_scope_pressure_escalates() -> None: case = development_case("dev-lightweight-material-under-decomposition") - routed = route_review_verdict( - allocation_gap(), - affected_region={ - "task_ids": ["task-light"], - "paths": ["src/new-owner.py"], - "interfaces": [], - "validation_oracles": ["VAL-NEW"], - }, - original_binding_identity={"binding_id": "binding-light", "sha256": "1" * 64}, - original_baseline_identity={"head": "2" * 40, "tree": "3" * 40}, - ) assert "new production owner and an independent validation boundary" in case["prompt"] assert case["expected_output"] == ( "Treats the task as materially under-decomposed and escalates to full orchestration " "instead of repeatedly expanding the lightweight plan." ) - assert routed["action"] == "reslice_plan" - assert routed["silent_expansion_allowed"] is False def test_pd_13_normal_lightweight_change_remains_one_disposable_plan() -> None: case = development_case("dev-lightweight-amendment-lane-separation") - result = TaskOwnershipScheduler(AvailableAdapter()).run_wave( - [candidate("task-light", "src/only.py")], completed=set() - ) assert "executor result, task state, review package, and archive record" in case["prompt"] assert case["expected_output"] == ( "Allows only the exact bounded Files.Modify amendment and rejects heavy lifecycle " "artifacts; the lightweight lane remains one disposable plan." ) - assert result.dispatched == ("task-light",) def test_pd_14_equivalent_under_decomposition_routes_by_lane_without_widening() -> None: + assert_normative_case( + "PD-07", + prompt="Execution proves one task materially under-decomposed after its repair frontier separates into two independently owned regions.", + expected_output="Stops repeatedly enlarging the task, requires a return to the plan, and reslices only the affected region while preserving the original binding, baseline, accepted unaffected regions, and typed repair route.", + skill_path="skills/orch-create-implementation-plan/SKILL.md", + owning_clause="When execution proves a task materially under-decomposed, return to the plan and reslice only the affected region around the newly evidenced seam. Preserve the original binding, baseline, and accepted unaffected regions; do not repeatedly enlarge the task.", + ) routed = route_review_verdict( allocation_gap(), affected_region={ From 8dd6574416c8f46f188d9a8624f2fd056f933ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 20:59:02 +0800 Subject: [PATCH 21/48] test(wor-109): close PD runtime evidence gaps --- tests/test_wor109_planner_scenarios.py | 144 +++++++++++++++++++++---- 1 file changed, 122 insertions(+), 22 deletions(-) diff --git a/tests/test_wor109_planner_scenarios.py b/tests/test_wor109_planner_scenarios.py index df2c02a..75bf94a 100644 --- a/tests/test_wor109_planner_scenarios.py +++ b/tests/test_wor109_planner_scenarios.py @@ -2,7 +2,6 @@ import json import sys -from copy import deepcopy from pathlib import Path import pytest @@ -166,6 +165,17 @@ def test_pd_03_executor_acceptance_path_has_explicit_controller_authority( task = _task(tmp_path) binding = _binding(tmp_path) + persisted: list[dict[str, object]] = [] + monkeypatch.setattr( + execution_context, + "load_task_execution_binding", + lambda *_args: binding, + ) + monkeypatch.setattr( + execution_context, + "_persist_binding", + lambda value, _root: persisted.append(value), + ) monkeypatch.setattr( execution_context, "capture_repository_evidence", @@ -176,16 +186,17 @@ def test_pd_03_executor_acceptance_path_has_explicit_controller_authority( "status": "clean", }, ) - first = execution_context.build_accepted_task_result( - task, binding, _handoff(), _validated(), accepted_at="2026-09-07T00:00:00Z" - ) - historical = deepcopy(binding) - historical["ownership"]["history"].append({"event": "historical-audit"}) - second = execution_context.build_accepted_task_result( - task, historical, _handoff(), _validated(), accepted_at="2026-09-07T00:00:00Z" + accepted = execution_context.materialize_accepted_task_result( + tmp_path, + task, + _handoff(), + _validated(), + accepted_at="2026-09-07T00:00:00Z", ) - assert first == second - assert "mutation_events" not in repr(first) + assert persisted == [{**binding, "accepted_result": accepted}] + assert accepted["schema"] == "accepted-task-result-v1" + assert accepted["owner_identity"]["owner_kind"] == "subagent" + assert "mutation_events" not in repr(accepted) def test_pd_04_independently_repairable_entry_points_remain_distinct() -> None: @@ -249,6 +260,12 @@ def test_pd_08_planning_economics_are_derived_without_cardinality_judgment() -> ), stage_event("suite-first", event_type="suite_started", evaluation_id="eval-001"), stage_event("suite-rerun", event_type="suite_started", evaluation_id="eval-001"), + stage_event( + "plan-review", + stage="plan", + event_type="stage_completed", + review_id="review-plan", + ), stage_event( "green", event_type="suite_completed", @@ -266,6 +283,7 @@ def test_pd_08_planning_economics_are_derived_without_cardinality_judgment() -> result = derive_planning_economics(records, process_id="process-001", plan_id="plan-001") assert result["initial_cardinality"] == {"phases": 1, "tasks": 2} assert result["plan_revisions"] == 1 + assert result["plan_reviews"] == 1 assert result["scope_allocation_repairs"] == 1 assert result["task_review_repairs"] == 1 assert result["validation_reruns"] == 1 @@ -419,7 +437,9 @@ def test_pd_13_normal_lightweight_change_remains_one_disposable_plan() -> None: ) -def test_pd_14_equivalent_under_decomposition_routes_by_lane_without_widening() -> None: +def test_pd_14_equivalent_under_decomposition_routes_by_lane_without_widening( + tmp_path: Path, +) -> None: assert_normative_case( "PD-07", prompt="Execution proves one task materially under-decomposed after its repair frontier separates into two independently owned regions.", @@ -427,20 +447,100 @@ def test_pd_14_equivalent_under_decomposition_routes_by_lane_without_widening() skill_path="skills/orch-create-implementation-plan/SKILL.md", owning_clause="When execution proves a task materially under-decomposed, return to the plan and reslice only the affected region around the newly evidenced seam. Preserve the original binding, baseline, and accepted unaffected regions; do not repeatedly enlarge the task.", ) + binding = {"binding_id": "binding-task-003", "sha256": "1" * 64} + baseline = {"head": "2" * 40, "tree": "3" * 40} + unaffected = [ + { + "artifact_id": "task-001", + "revision": "1", + "sha256": ZERO_SHA, + "source_tree": ZERO_TREE, + }, + { + "artifact_id": "task-002", + "revision": "1", + "sha256": "4" * 64, + "source_tree": "5" * 40, + }, + ] + region = { + "task_ids": ["task-003"], + "paths": ["scripts/orchestration/review_runtime.py"], + "interfaces": ["API-PD-001"], + "validation_oracles": ["VAL-004"], + } routed = route_review_verdict( allocation_gap(), - affected_region={ - "task_ids": ["task-003"], - "paths": ["scripts/orchestration/review_runtime.py"], - "interfaces": ["API-PD-001"], - "validation_oracles": ["VAL-004"], - }, - original_binding_identity={"binding_id": "binding-task-003", "sha256": "1" * 64}, - original_baseline_identity={"head": "2" * 40, "tree": "3" * 40}, + affected_region=region, + unaffected_evidence_identities=unaffected, + original_binding_identity=binding, + original_baseline_identity=baseline, ) assert routed["action"] == "reslice_plan" + assert routed["affected_region"] == region + assert routed["preserved_evidence_identities"] == unaffected assert routed["silent_expansion_allowed"] is False assert routed["preserve_valid_work_and_evidence"] is True - case = development_case("dev-lightweight-material-under-decomposition") - assert "escalates to full orchestration" in case["expected_output"] - assert "instead of repeatedly expanding" in case["expected_output"] + + orch = tmp_path / ".work-bundle/orchestration" + spec = orch / "spec/active/spec.md" + plan = orch / "plan/active/plan.md" + spec.parent.mkdir(parents=True) + plan.parent.mkdir(parents=True) + spec.write_text("---\nid: spec-test\nstatus: verified\n---\nAuthority\n", encoding="utf-8") + plan.write_text( + "---\nid: plan-001\nstatus: Planned\nsource_spec: [spec-test]\n---\nResliced\n", + encoding="utf-8", + ) + with pytest.raises(ReviewContractError, match="accepted repaired plan-review authority"): + resume_plan_return( + routed, + workspace_root=tmp_path, + plan_path=plan, + current_binding_identity=binding, + current_baseline_identity=baseline, + current_unaffected_evidence_identities=unaffected, + ) + + review = { + "review_id": "review-plan-pd14", + "stage": "plan", + "target_identity": plan_review_identity(tmp_path, plan), + "reviewer": { + "agent_id": "reviewer-pd14", + "capability": "judgment", + "authorship": "none", + "repair_participation": "none", + "decision_participation": "none", + "deliberation_participation": "none", + "context_origin": "direct_source", + }, + "evidence": { + "mode": "direct", + "capabilities": ["source inspection"], + "unavailable_evidence": [], + "commands": [], + "artifacts": [], + }, + "verdict": "accepted", + "findings": [], + "started_at": "2026-09-07T00:00:00Z", + "completed_at": "2026-09-07T00:01:00Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + review = bind_review_receipt(tmp_path, review) + reviews = orch / "reviews" + reviews.mkdir() + (reviews / "pd14-plan.json").write_text(json.dumps(review), encoding="utf-8") + + resumed = resume_plan_return( + routed, + workspace_root=tmp_path, + plan_path=plan, + current_binding_identity=binding, + current_baseline_identity=baseline, + current_unaffected_evidence_identities=unaffected, + ) + assert resumed["execution_state"] == "ready_from_repaired_authority" + assert resumed["affected_region"] == region + assert resumed["preserved_evidence_identities"] == unaffected From 9c3f71b2b7dfc7beb763d9d91ae1fcd6fc76ccef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Mon, 7 Sep 2026 21:21:44 +0800 Subject: [PATCH 22/48] test(orchestration): bind lightweight planner scenarios --- tests/test_wor109_planner_scenarios.py | 80 +++++++++++++++++++++----- 1 file changed, 66 insertions(+), 14 deletions(-) diff --git a/tests/test_wor109_planner_scenarios.py b/tests/test_wor109_planner_scenarios.py index 75bf94a..9ee0b64 100644 --- a/tests/test_wor109_planner_scenarios.py +++ b/tests/test_wor109_planner_scenarios.py @@ -104,6 +104,23 @@ def assert_normative_case( assert owning_clause in read(skill_path) +def assert_development_case( + case_id: str, + *, + prompt: str, + expected_output: str, + owning_clauses: tuple[str, ...], +) -> None: + assert development_case(case_id) == { + "id": case_id, + "prompt": prompt, + "expected_output": expected_output, + } + skill = read("skills/dev-create-task-plan/SKILL.md") + for clause in owning_clauses: + assert clause in skill + + def allocation_gap() -> dict[str, object]: return { "finding_id": "finding-under-decomposed", @@ -413,33 +430,68 @@ def test_pd_10_hypothetical_defects_do_not_create_speculative_tasks() -> None: def test_pd_11_same_owner_pre_mutation_path_amendment_stays_lightweight() -> None: - case = development_case("dev-lightweight-pre-mutation-one-file-amendment") - assert "Before mutation" in case["prompt"] - assert "same implementation owner" in case["prompt"] - assert "Amends Files.Modify once" in case["expected_output"] + assert_development_case( + "dev-lightweight-pre-mutation-one-file-amendment", + prompt="Before mutation, source grounding shows that a lightweight plan must add one exact file owned by the same implementation owner; purpose, accepted authority, expected delta, impact radius, ownership, validation boundary, and completion claim are materially unchanged.", + expected_output="Amends Files.Modify once with the exact additional path and records the supporting evidence before the first write, while keeping the same disposable lightweight plan.", + owning_clauses=( + "Before the first write, one explicit plan amendment may add exactly one additional path to `Files.Modify` when it has the same implementation owner and purpose, decision authority, expected delta, impact radius, ownership, validation boundary, and completion claim remain materially unchanged.", + "Record the exact path and supporting evidence in the existing disposable plan.", + ), + ) def test_pd_12_material_lightweight_scope_pressure_escalates() -> None: - case = development_case("dev-lightweight-material-under-decomposition") - assert "new production owner and an independent validation boundary" in case["prompt"] - assert case["expected_output"] == ( - "Treats the task as materially under-decomposed and escalates to full orchestration " - "instead of repeatedly expanding the lightweight plan." + assert_development_case( + "dev-lightweight-material-under-decomposition", + prompt="Execution reveals that the proposed extra file introduces a new production owner and an independent validation boundary.", + expected_output="Treats the task as materially under-decomposed and escalates to full orchestration instead of repeatedly expanding the lightweight plan.", + owning_clauses=( + "If new evidence makes the task materially under-decomposed—a new production or lifecycle owner, independent validation boundary, wide impact, API or workflow decision, second repository, or barrier or convergence topology—stop and escalate to full orchestration.", + ), + ) + assert_development_case( + "dev-lightweight-amendment-after-mutation", + prompt="A lightweight task has already mutated an authorized file when it discovers one more file that would otherwise satisfy the bounded amendment conditions.", + expected_output="Does not amend the mutation envelope after mutation has begun; stops and escalates to full orchestration with the concrete scope evidence.", + owning_clauses=( + "The amendment must not be repeated or made after mutation begins.", + ), ) def test_pd_13_normal_lightweight_change_remains_one_disposable_plan() -> None: - case = development_case("dev-lightweight-amendment-lane-separation") - assert "executor result, task state, review package, and archive record" in case["prompt"] - assert case["expected_output"] == ( - "Allows only the exact bounded Files.Modify amendment and rejects heavy lifecycle " - "artifacts; the lightweight lane remains one disposable plan." + assert_development_case( + "dev-lightweight-algorithm-not-settled", + prompt="Plan a bounded mechanical change whose algorithm is not yet chosen, while purpose, accepted authority, expected delta, and impact radius are settled.", + expected_output="Allows the disposable lightweight plan because eligibility does not require a settled implementation strategy; it does not import executor-result, Completed, or a review package. Eval JSON stores this as a pressure scenario; presence is not executed agent-behavior proof.", + owning_clauses=( + "Create a bounded mechanical plan when purpose, accepted or `none relevant` authority, expected delta, and impact radius are settled even if the internal algorithm is not chosen.", + "Eligibility does not require the internal implementation strategy to be settled.", + ), + ) + assert_development_case( + "dev-lightweight-amendment-lane-separation", + prompt="A pre-mutation one-file amendment remains same-owner and mechanically bounded, but the agent proposes adding an executor result, task state, review package, and archive record for assurance.", + expected_output="Allows only the exact bounded Files.Modify amendment and rejects heavy lifecycle artifacts; the lightweight lane remains one disposable plan.", + owning_clauses=( + "Keep one disposable `.work-bundle/runtime/dev-plans/` artifact.", + "Do not import executor-result, `Completed`, review package, archive helper, or heavy Knowledge Base Update closure into the lightweight lane.", + ), ) def test_pd_14_equivalent_under_decomposition_routes_by_lane_without_widening( tmp_path: Path, ) -> None: + assert_development_case( + "dev-lightweight-material-under-decomposition", + prompt="Execution reveals that the proposed extra file introduces a new production owner and an independent validation boundary.", + expected_output="Treats the task as materially under-decomposed and escalates to full orchestration instead of repeatedly expanding the lightweight plan.", + owning_clauses=( + "If new evidence makes the task materially under-decomposed—a new production or lifecycle owner, independent validation boundary, wide impact, API or workflow decision, second repository, or barrier or convergence topology—stop and escalate to full orchestration.", + ), + ) assert_normative_case( "PD-07", prompt="Execution proves one task materially under-decomposed after its repair frontier separates into two independently owned regions.", From 72db1c588504efd136dcbbf86ee019a67ac97d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 04:56:39 +0800 Subject: [PATCH 23/48] fix(orchestration): preserve accepted task authority --- evals/wor109/contracts-v1.schema.json | 20 --- evals/wor109/fixtures.json | 20 --- evals/wor109/migration-impact.json | 31 ---- evals/wor109/verify.py | 140 ---------------- scripts/orchestration/execution_context.py | 54 ++++++- scripts/orchestration/plans.py | 61 ++++--- ...tweight.py => test_dev_task_plan_scope.py} | 0 ... => test_orchestration_accepted_result.py} | 49 ++++++ ...rchestration_accepted_result_lifecycle.py} | 152 ++++++++++++++++-- ...e.py => test_orchestration_plan_return.py} | 0 ... test_orchestration_planning_contracts.py} | 0 ... test_orchestration_planning_scenarios.py} | 2 +- .../test_orchestration_workflow_contracts.py | 54 +++++-- tests/test_wor109_closure.py | 52 ------ 14 files changed, 316 insertions(+), 319 deletions(-) delete mode 100644 evals/wor109/contracts-v1.schema.json delete mode 100644 evals/wor109/fixtures.json delete mode 100644 evals/wor109/migration-impact.json delete mode 100644 evals/wor109/verify.py rename tests/{test_wor109_lightweight.py => test_dev_task_plan_scope.py} (100%) rename tests/{test_wor109_accepted_result.py => test_orchestration_accepted_result.py} (84%) rename tests/{test_wor109_lifecycle.py => test_orchestration_accepted_result_lifecycle.py} (55%) rename tests/{test_wor109_planner_runtime.py => test_orchestration_plan_return.py} (100%) rename tests/{test_wor109_planner_contracts.py => test_orchestration_planning_contracts.py} (100%) rename tests/{test_wor109_planner_scenarios.py => test_orchestration_planning_scenarios.py} (99%) delete mode 100644 tests/test_wor109_closure.py diff --git a/evals/wor109/contracts-v1.schema.json b/evals/wor109/contracts-v1.schema.json deleted file mode 100644 index 65d88e7..0000000 --- a/evals/wor109/contracts-v1.schema.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "$id": "urn:work-bundle:wor109:closure-contracts:v1", - "type": "object", - "additionalProperties": false, - "required": ["fixture"], - "properties": { - "fixture": { - "type": "object", - "additionalProperties": false, - "required": ["id", "scenario", "oracle", "source_ids", "pytest_node"], - "properties": { - "id": {"type": "string", "pattern": "^PD-(0[1-9]|1[0-4])$"}, - "scenario": {"type": "string"}, - "oracle": {"type": "string"}, - "source_ids": {"type": "array", "items": {"type": "string"}}, - "pytest_node": {"type": "string"} - } - } - } -} diff --git a/evals/wor109/fixtures.json b/evals/wor109/fixtures.json deleted file mode 100644 index 1526769..0000000 --- a/evals/wor109/fixtures.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "contract": "wor109-closure-fixtures-v1", - "evaluation_id": "wor109-review-stable-orchestration-v1", - "fixtures": [ - {"id":"PD-01","scenario":"coarse plan versus larger plan with evidenced independent runtime entry points","oracle":"larger plan allowed only for independently repairable seams; cardinality has no reward","source_ids":["REQ-PD-001","REQ-PD-004"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_01_cardinality_never_overrides_evidenced_runtime_seams"}, - {"id":"PD-02","scenario":"task owns a review/runtime helper but omits its known production lifecycle surface","oracle":"planner contracts require an authoritative production owner","source_ids":["REQ-PD-001","REQ-PD-002"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_02_helper_allocation_cannot_leave_production_lifecycle_unowned"}, - {"id":"PD-03","scenario":"scheduler/ownership helper and unit fixture exist but executor acceptance path is unassigned","oracle":"production acceptance authority is explicitly bound","source_ids":["REQ-PD-002"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_03_executor_acceptance_path_has_explicit_controller_authority"}, - {"id":"PD-04","scenario":"two production entry points implement one requirement but fail and repair independently","oracle":"independent repair identities remain distinct","source_ids":["REQ-PD-001","REQ-PD-005"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_04_independently_repairable_entry_points_remain_distinct"}, - {"id":"PD-05","scenario":"single-file mechanical change has one scope, oracle, owner, and repair frontier","oracle":"coherent mechanical increments are not micro-tasked","source_ids":["REQ-PD-004","REQ-PD-005"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_05_coherent_mechanical_increment_is_not_micro_tasked_by_file_count"}, - {"id":"PD-06","scenario":"two independent producers feed one convergence consumer","oracle":"actual barriers and convergence are represented","source_ids":["REQ-PD-005"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_06_producer_convergence_requires_a_real_barrier_and_owner"}, - {"id":"PD-07","scenario":"load-bearing telemetry/oracle field is omitted solely for brevity","oracle":"complete nonredundant authority retains load-bearing fields","source_ids":["REQ-SPEC-001"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_07_load_bearing_specification_authority_survives_compaction"}, - {"id":"PD-08","scenario":"one plan proceeds through reviews, scope repairs, task repairs, reruns, and final acceptance","oracle":"planning economics are emitted without cardinality judgment","source_ids":["REQ-PD-006"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_08_planning_economics_are_derived_without_cardinality_judgment"}, - {"id":"PD-09","scenario":"review proves a coherent task needs a separately owned production path or validation boundary","oracle":"affected region returns to plan owner while unaffected evidence is preserved","source_ids":["REQ-PD-003","CON-003"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_09_under_decomposition_returns_only_the_affected_plan_region"}, - {"id":"PD-10","scenario":"tasks are proposed only for hypothetical future defects without a current seam","oracle":"speculative fragments are not added","source_ids":["REQ-PD-004"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_10_hypothetical_defects_do_not_create_speculative_tasks"}, - {"id":"PD-11","scenario":"lightweight task discovers one same-owner file with unchanged authority and validation","oracle":"one exact pre-mutation amendment remains lightweight","source_ids":["REQ-LW-001","REQ-LW-003"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_11_same_owner_pre_mutation_path_amendment_stays_lightweight"}, - {"id":"PD-12","scenario":"lightweight work discovers a new owner, boundary, wide impact, API decision, repository, or barrier","oracle":"material scope pressure escalates to full orchestration","source_ids":["REQ-LW-002"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_12_material_lightweight_scope_pressure_escalates"}, - {"id":"PD-13","scenario":"normal eligible lightweight change runs after WOR-109","oracle":"one disposable plan remains isolated from heavy machinery","source_ids":["REQ-LW-003"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_13_normal_lightweight_change_remains_one_disposable_plan"}, - {"id":"PD-14","scenario":"equivalent under-decomposition evidence reaches heavy and lightweight lanes","oracle":"heavy reslices and lightweight escalates without silent widening","source_ids":["REQ-PD-003","REQ-LW-002"],"pytest_node":"tests/test_wor109_planner_scenarios.py::test_pd_14_equivalent_under_decomposition_routes_by_lane_without_widening"} - ] -} diff --git a/evals/wor109/migration-impact.json b/evals/wor109/migration-impact.json deleted file mode 100644 index 3c6f82d..0000000 --- a/evals/wor109/migration-impact.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "contract": "wor109-migration-impact-v1", - "issue": "WOR-109", - "baseline": {"commit": "cfa089f0d2ed211b98d049eb37bfcdccb8091516", "tree": "12e4a696c3caf991654f0b9ac9ef40594699c8d4"}, - "accepted_worktree": {"commit": "3cf749bbb34b208ca852a5b77bcf26645a50d4cb", "tree": "e343bf27f41996bd8189949ae6749f48abc09652", "status": "clean"}, - "changed_surfaces": { - "added": {"skills": [], "rules": [], "contracts": [], "runtime_operations": [], "artifact_semantics": [], "evaluations": ["evals/wor109/contracts-v1.schema.json", "evals/wor109/fixtures.json", "evals/wor109/migration-impact.json", "evals/wor109/verify.py", "tests/test_wor109_accepted_result.py", "tests/test_wor109_closure.py", "tests/test_wor109_lifecycle.py", "tests/test_wor109_lightweight.py", "tests/test_wor109_planner_contracts.py", "tests/test_wor109_planner_runtime.py"]}, - "modified": {"skills": ["skills/dev-create-task-plan/SKILL.md", "skills/orch-create-implementation-plan/SKILL.md", "skills/orch-create-specification/SKILL.md"], "rules": ["rules/orchestration/orch-artifact-authoring.md"], "contracts": ["references/assets/orchestration/contract/plan-v1.md", "references/assets/orchestration/contract/stage-event-v1.schema.json"], "runtime_operations": ["scripts/orchestration/dispatcher.py", "scripts/orchestration/execution_context.py", "scripts/orchestration/plans.py", "scripts/orchestration/review_runtime.py", "scripts/orchestration/task_ownership.py", "scripts/work-bundle/stage_events.py"], "artifact_semantics": ["evals/wor105/components/native-transition-record.yaml", "evals/wor105/freeze-manifest.json", "evals/wor105/results.jsonl", "evals/wor108/migration-impact.json", "references/assets/orchestration/workflow.md", "references/evals/development/evals.json", "references/evals/orchestration/evals.json", "tests/test_orchestration_reviews.py", "tests/test_orchestration_skill_rule_boundary.py", "tests/test_orchestration_workflow_contracts.py", "tests/test_wor105_native_transition.py", "tests/test_wor108_closure.py", "tests/test_wor108_context_projection.py"], "evaluations": []}, - "deleted": {"skills": [], "rules": [], "contracts": [], "runtime_operations": [], "artifact_semantics": [], "evaluations": []} - }, - "semantic_deltas": { - "acceptance_authority": {"source_ids": ["REQ-AR-001", "REQ-AR-002"], "affected_paths": ["scripts/orchestration/execution_context.py", "tests/test_wor109_accepted_result.py"]}, - "planner_reslice": {"source_ids": ["REQ-PD-001", "REQ-PD-003"], "affected_paths": ["skills/orch-create-implementation-plan/SKILL.md", "scripts/orchestration/plans.py"]}, - "specification_wording": {"source_ids": ["REQ-SPEC-001"], "affected_paths": ["skills/orch-create-specification/SKILL.md"]}, - "lightweight_scope": {"source_ids": ["REQ-LW-001", "REQ-LW-002"], "affected_paths": ["skills/dev-create-task-plan/SKILL.md"]}, - "historical_identity": {"source_ids": ["REQ-ID-001"], "affected_paths": ["tests/test_wor109_lifecycle.py"]} - }, - "parity_owners": { - "WOR-76": {"status": "likely", "navigation": "Review accepted-result authority and lifecycle projection consumers."}, - "WOR-78": {"status": "likely", "navigation": "Review planner decomposition and bounded reslice contracts."}, - "WOR-81": {"status": "likely", "navigation": "Review lightweight scope-control lane semantics."}, - "WOR-82": {"status": "likely", "navigation": "Review deterministic integrated evaluation and closure gates."}, - "WOR-83": {"status": "likely", "navigation": "Review planning telemetry and diagnostics emitted by stage-event convergence."} - }, - "epoch1_evidence": { - "reusable": ["WOR-108 closure fixtures", "baseline identity cfa089f0d2ed211b98d049eb37bfcdccb8091516"], - "invalidated": [{"evidence_id": "transient handoff acceptance evidence", "reason": "acceptance authority is now persisted once", "replacement": "accepted-result projection digests"}] - }, - "evaluation": {"fixture_ids": ["PD-01", "PD-02", "PD-03", "PD-04", "PD-05", "PD-06", "PD-07", "PD-08", "PD-09", "PD-10", "PD-11", "PD-12", "PD-13", "PD-14"], "verifier_output_digest": "9789fd03efdec6253e13d8c6a95e678c81765f394732d98775b13ff66e629462"}, - "exclusions": {"work-bundle-mcp": {"authorized": false}, "WOR-107": {"authorized": false}, "WOR-79": {"authorized": false}, "Step 00 mutation": {"authorized": false}, "migration execution": {"authorized": false}} -} diff --git a/evals/wor109/verify.py b/evals/wor109/verify.py deleted file mode 100644 index 6403ab9..0000000 --- a/evals/wor109/verify.py +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env python3 -"""Independent deterministic verifier for the WOR-109 closure package.""" -from __future__ import annotations - -import argparse -import hashlib -import json -import re -import subprocess -from pathlib import Path -from typing import Any - -EVAL_ROOT = Path(__file__).resolve().parent -REPO_ROOT = EVAL_ROOT.parents[1] -BASELINE = {"commit": "cfa089f0d2ed211b98d049eb37bfcdccb8091516", "tree": "12e4a696c3caf991654f0b9ac9ef40594699c8d4"} -EXPECTED_IDS = tuple(f"PD-{i:02d}" for i in range(1, 15)) -SOURCE_IDS = {"REQ-PD-001", "REQ-PD-002", "REQ-PD-003", "REQ-PD-004", "REQ-PD-005", "REQ-PD-006", "REQ-SPEC-001", "REQ-LW-001", "REQ-LW-002", "REQ-LW-003", "CON-003"} -FIXTURE_KEYS = {"id", "scenario", "oracle", "source_ids", "pytest_node"} -MIGRATION_KEYS = {"contract", "issue", "baseline", "accepted_worktree", "changed_surfaces", "semantic_deltas", "parity_owners", "epoch1_evidence", "evaluation", "exclusions"} -CATEGORIES = {"skills", "rules", "contracts", "runtime_operations", "artifact_semantics", "evaluations"} -OPERATIONS = {"added", "modified", "deleted"} -NODE = re.compile(r"^tests/test_wor109_[a-z_]+\.py::test_[a-z0-9_]+$") - -class VerificationError(RuntimeError): - pass - -def _load(path: Path) -> dict[str, Any]: - value = json.loads(path.read_text(encoding="utf-8")) - if not isinstance(value, dict): - raise VerificationError(f"object required: {path}") - return value - -def _verify_schema(path: Path) -> None: - schema = _load(path) - if schema.get("$id") != "urn:work-bundle:wor109:closure-contracts:v1" or schema.get("additionalProperties") is not False: - raise VerificationError("closure schema identity or closed shape mismatch") - fixture = schema.get("properties", {}).get("fixture", {}) - if fixture.get("additionalProperties") is not False or set(fixture.get("required", [])) != FIXTURE_KEYS: - raise VerificationError("fixture schema is not closed") - -def _verify_fixtures(path: Path) -> tuple[str, ...]: - package = _load(path) - if set(package) != {"contract", "evaluation_id", "fixtures"} or package["contract"] != "wor109-closure-fixtures-v1" or package["evaluation_id"] != "wor109-review-stable-orchestration-v1": - raise VerificationError("fixture package identity or shape mismatch") - fixtures = package["fixtures"] - if not isinstance(fixtures, list) or tuple(item.get("id") for item in fixtures if isinstance(item, dict)) != EXPECTED_IDS: - raise VerificationError("fixture IDs must be exactly PD-01..PD-14") - nodes: set[str] = set() - for item in fixtures: - if set(item) != FIXTURE_KEYS or not item["scenario"] or not item["oracle"]: - raise VerificationError(f"fixture closed shape mismatch: {item.get('id')}") - if not isinstance(item["source_ids"], list) or not item["source_ids"] or not set(item["source_ids"]).issubset(SOURCE_IDS): - raise VerificationError(f"fixture source authority mismatch: {item['id']}") - node = item["pytest_node"] - if not NODE.fullmatch(node) or node in nodes: - raise VerificationError(f"invalid or duplicate pytest node: {node}") - semantic_prefix = ( - "tests/test_wor109_planner_scenarios.py::" - f"test_pd_{item['id'][3:]}_" - ) - if not node.startswith(semantic_prefix): - raise VerificationError(f"semantic oracle binding mismatch: {item['id']}") - nodes.add(node) - test_path, function = node.split("::") - source = REPO_ROOT / test_path - if not source.is_file() or f"def {function}(" not in source.read_text(encoding="utf-8"): - raise VerificationError(f"pytest oracle does not resolve: {node}") - return EXPECTED_IDS - -def _git_changed() -> dict[str, str]: - proc = subprocess.run(["git", "-C", str(REPO_ROOT), "diff", "--name-status", BASELINE["commit"], "HEAD", "--"], capture_output=True, text=True, check=False) - if proc.returncode: - raise VerificationError("baseline delta unavailable") - result: dict[str, str] = {} - for line in proc.stdout.splitlines(): - status, path = line.split("\t", 1) - result[path] = {"A": "added", "M": "modified", "D": "deleted"}.get(status, "modified") - return result - -def _verify_migration(path: Path, fixture_ids: tuple[str, ...]) -> int: - manifest = _load(path) - if set(manifest) != MIGRATION_KEYS or manifest["contract"] != "wor109-migration-impact-v1" or manifest["issue"] != "WOR-109" or manifest["baseline"] != BASELINE: - raise VerificationError("migration identity or closed shape mismatch") - accepted = manifest["accepted_worktree"] - if set(accepted) != {"commit", "tree", "status"} or accepted["commit"] != "3cf749bbb34b208ca852a5b77bcf26645a50d4cb" or accepted["tree"] != "e343bf27f41996bd8189949ae6749f48abc09652": - raise VerificationError("accepted pre-commit worktree mismatch") - surfaces = manifest["changed_surfaces"] - if set(surfaces) != OPERATIONS or any(set(group) != CATEGORIES for group in surfaces.values()): - raise VerificationError("changed surface dimensions mismatch") - listed: dict[str, str] = {} - for op, groups in surfaces.items(): - for category, paths in groups.items(): - if not isinstance(paths, list) or paths != sorted(set(paths)): - raise VerificationError("changed surfaces must be sorted and unique") - for rel in paths: - if rel in listed or (op != "deleted" and not (REPO_ROOT / rel).is_file()): - raise VerificationError(f"invalid changed surface: {rel}") - listed[rel] = op - if listed != _git_changed(): - raise VerificationError("changed surface completeness mismatch") - if set(manifest["semantic_deltas"]) != {"acceptance_authority", "planner_reslice", "specification_wording", "lightweight_scope", "historical_identity"}: - raise VerificationError("semantic delta rows mismatch") - for row in manifest["semantic_deltas"].values(): - if set(row) != {"source_ids", "affected_paths"} or not row["source_ids"] or not row["affected_paths"]: - raise VerificationError("semantic delta row incomplete") - telemetry_changed = any(path in listed for path in ("scripts/work-bundle/stage_events.py", "scripts/orchestration/review_runtime.py")) - expected_parity = {"WOR-76", "WOR-78", "WOR-81", "WOR-82"} | ({"WOR-83"} if telemetry_changed else set()) - if set(manifest["parity_owners"]) != expected_parity or any(set(row) != {"status", "navigation"} or row["status"] != "likely" for row in manifest["parity_owners"].values()): - raise VerificationError("parity owner rows mismatch") - evidence = manifest["epoch1_evidence"] - if set(evidence) != {"reusable", "invalidated"} or not isinstance(evidence["reusable"], list) or not isinstance(evidence["invalidated"], list): - raise VerificationError("epoch-1 evidence partition mismatch") - for row in evidence["invalidated"]: - if set(row) != {"evidence_id", "reason", "replacement"} or not row["reason"] or not row["replacement"]: - raise VerificationError("invalidated evidence requires reason and replacement") - evaluation = manifest["evaluation"] - if set(evaluation) != {"fixture_ids", "verifier_output_digest"} or tuple(evaluation["fixture_ids"]) != fixture_ids or not re.fullmatch(r"[0-9a-f]{64}", evaluation["verifier_output_digest"]): - raise VerificationError("evaluation binding mismatch") - exclusions = manifest["exclusions"] - if set(exclusions) != {"work-bundle-mcp", "WOR-107", "WOR-79", "Step 00 mutation", "migration execution"} or any(row != {"authorized": False} for row in exclusions.values()): - raise VerificationError("exclusion authorization mismatch") - return len(listed) - -def verify(fixtures_path: Path = EVAL_ROOT / "fixtures.json", migration_path: Path = EVAL_ROOT / "migration-impact.json", schema_path: Path = EVAL_ROOT / "contracts-v1.schema.json") -> dict[str, Any]: - _verify_schema(schema_path) - ids = _verify_fixtures(fixtures_path) - changed = _verify_migration(migration_path, ids) - return {"evaluation_id": "wor109-review-stable-orchestration-v1", "fixtures": len(ids), "changed_surfaces": changed, "verdict": "accepted"} - -def main() -> int: - parser = argparse.ArgumentParser() - parser.add_argument("--fixtures", type=Path, default=EVAL_ROOT / "fixtures.json") - parser.add_argument("--migration-impact", type=Path, default=EVAL_ROOT / "migration-impact.json") - parser.add_argument("--schema", type=Path, default=EVAL_ROOT / "contracts-v1.schema.json") - args = parser.parse_args() - print(json.dumps(verify(args.fixtures, args.migration_impact, args.schema), sort_keys=True)) - return 0 - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index bf343f8..7a7353d 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -1396,12 +1396,16 @@ def load_task_execution_binding(control_root: Path, plan_id: str, task_id: str) ACCEPTED_TASK_RESULT_SCHEMA = "accepted-task-result-v1" -ACCEPTED_TASK_RESULT_FIELDS = { +LEGACY_ACCEPTED_TASK_RESULT_FIELDS = { "schema", "plan_id", "task_id", "binding_id", "baseline_identity", "accepted_source", "authority_projection", "executor_result_digest", "validation_evidence_ids", "review_id", "owner_identity", "accepted_at", "invalidation", } +ACCEPTED_TASK_RESULT_FIELDS = { + *LEGACY_ACCEPTED_TASK_RESULT_FIELDS, + "knowledge_disposition", +} ACCEPTED_AUTHORITY_PROJECTION_FIELDS = { "task_digest", "binding_digest", "scope_digest", "validation_obligations_digest", "required_review_digest", "ownership_digest", @@ -1528,15 +1532,19 @@ def _accepted_source_state_digest( head: object, tree: object, authority_projection: Mapping[str, Any], + knowledge_disposition: Mapping[str, Any] | None = None, ) -> str: - return semantic_digest({ + state = { "plan_id": plan_id, "task_id": task_id, "binding_id": binding_id, "baseline_identity": dict(baseline_identity), "accepted_source": {"head": head, "tree": tree}, "authority_projection": dict(authority_projection), - }) + } + if knowledge_disposition is not None: + state["knowledge_disposition"] = dict(knowledge_disposition) + return semantic_digest(state) def build_accepted_task_result( @@ -1595,6 +1603,10 @@ def build_accepted_task_result( accepted_review=review, owner_identity=accepted_ownership, ) + knowledge_disposition = validated.get("knowledge_disposition") + if not isinstance(knowledge_disposition, Mapping): + raise SystemExit("accepted task result requires validated knowledge disposition") + knowledge_disposition = dict(knowledge_disposition) evidence = capture_repository_evidence(Path(str(binding.get("execution_path") or "")).resolve()) accepted_source = {"head": evidence.get("head"), "tree": evidence.get("tree")} accepted_source["state_digest"] = _accepted_source_state_digest( @@ -1605,6 +1617,7 @@ def build_accepted_task_result( head=accepted_source["head"], tree=accepted_source["tree"], authority_projection=authority_projection, + knowledge_disposition=knowledge_disposition, ) return { "schema": ACCEPTED_TASK_RESULT_SCHEMA, @@ -1623,6 +1636,7 @@ def build_accepted_task_result( "validation_evidence_ids": validation_ids, "review_id": review_id, "owner_identity": accepted_ownership, + "knowledge_disposition": knowledge_disposition, "accepted_at": accepted_at or datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), "invalidation": None, } @@ -1635,7 +1649,11 @@ def assert_accepted_task_result_current( if accepted.get("schema") != ACCEPTED_TASK_RESULT_SCHEMA: raise SystemExit("accepted task result schema is invalid") - if set(accepted) != ACCEPTED_TASK_RESULT_FIELDS: + accepted_fields = frozenset(accepted) + if accepted_fields not in { + frozenset(LEGACY_ACCEPTED_TASK_RESULT_FIELDS), + frozenset(ACCEPTED_TASK_RESULT_FIELDS), + }: raise SystemExit("accepted task result shape is not closed") if accepted.get("invalidation") is not None: raise SystemExit("accepted task result was explicitly invalidated") @@ -1648,6 +1666,29 @@ def assert_accepted_task_result_current( raise SystemExit("accepted task result authority projection is not closed") if not isinstance(owner_identity, Mapping): raise SystemExit("accepted task result owner identity is invalid") + knowledge_disposition = accepted.get("knowledge_disposition") + if "knowledge_disposition" in accepted: + if not isinstance(knowledge_disposition, Mapping): + raise SystemExit("accepted task result knowledge disposition is invalid") + task_files = task.get("files") if isinstance(task.get("files"), Mapping) else {} + truth_basis = task.get("truth_basis") if isinstance(task.get("truth_basis"), Mapping) else {} + try: + current_disposition = _validated_knowledge_disposition( + {"knowledge_disposition": dict(knowledge_disposition)}, + [str(value) for value in _as_list(task.get("source_ids"))], + [ + str(value) + for value in [ + *_as_list(task_files.get("read")), + *_as_list(task_files.get("write")), + ] + ], + _allocated_decision_aliases(truth_basis), + ) + except SystemExit as error: + raise SystemExit("accepted task result knowledge disposition is invalid") from error + if dict(knowledge_disposition) != current_disposition: + raise SystemExit("accepted task result knowledge disposition is invalid") current_projection = { "task_digest": semantic_digest(_accepted_task_projection(task)), "binding_digest": semantic_digest(_accepted_binding_projection(binding)), @@ -1686,6 +1727,11 @@ def assert_accepted_task_result_current( head=accepted_source.get("head"), tree=accepted_source.get("tree"), authority_projection=authority_projection, + knowledge_disposition=( + knowledge_disposition + if isinstance(knowledge_disposition, Mapping) + else None + ), ), } for label, current in checks.items(): diff --git a/scripts/orchestration/plans.py b/scripts/orchestration/plans.py index 77fb513..461fc75 100644 --- a/scripts/orchestration/plans.py +++ b/scripts/orchestration/plans.py @@ -50,7 +50,25 @@ def _assert_archive_knowledge_gate( if upstream is None: raise SystemExit("knowledge-blocked: plan has no Knowledge Base Update disposition") closure_return = _plan_knowledge_field(body, "Closure return") or "missing" - handoffs = [handoff for handoff, _brief in validated] + legacy = [result for result, _brief in validated if "knowledge_disposition" not in result] + if legacy: + if upstream == "required" and closure_return == "completed": + return + raise SystemExit( + "knowledge-blocked: legacy accepted results require plan-level required/completed closure" + ) + handoffs = [ + { + "related": {"plan": plan_id, "task": result.get("task_id")}, + "result": {"state": "completed"}, + "acceptance_review": { + "required": brief.get("review_required") is True, + "verdict": "accept", + }, + "knowledge_disposition": result.get("knowledge_disposition"), + } + for result, brief in validated + ] review_required_by_task = { str(brief.get("task_id") or ""): brief.get("review_required") is True for _handoff, brief in validated } @@ -145,14 +163,14 @@ def _validated_plan_task_handoffs( return validated -def _plan_section_table(body: str, name: str) -> list[list[str]]: +def _plan_section_table_parts(body: str, name: str) -> tuple[list[str], list[list[str]]]: section = re.search( rf"^##\s+(?:\d+(?:\.\d+)*\.?\s+)?{re.escape(name)}\s*$([\s\S]*?)(?=^##\s|\Z)", body, re.MULTILINE, ) if not section: - return [] + return [], [] rows: list[list[str]] = [] for line in section.group(1).splitlines(): if not line.strip().startswith("|"): @@ -161,18 +179,29 @@ def _plan_section_table(body: str, name: str) -> list[list[str]]: if not cells or all(re.fullmatch(r":?-{3,}:?", cell) for cell in cells): continue rows.append(cells) - return rows[1:] if rows else [] + return (rows[0], rows[1:]) if rows else ([], []) + + +def _plan_section_table(body: str, name: str) -> list[list[str]]: + _header, rows = _plan_section_table_parts(body, name) + return rows def _declared_integration_commands(body: str) -> list[str]: commands: list[str] = [] - for cells in _plan_section_table(body, "Tests"): - if len(cells) < 6: + header, rows = _plan_section_table_parts(body, "Tests") + normalized = [re.sub(r"\s+", " ", cell.strip().lower()) for cell in header] + if "test type" not in normalized or "command" not in normalized: + return [] + test_type_index = normalized.index("test type") + command_index = normalized.index("command") + for cells in rows: + if max(test_type_index, command_index) >= len(cells): continue - test_type = cells[1].lower() + test_type = cells[test_type_index].lower() if "integration" not in test_type or "unit|integration" in test_type: continue - command = cells[5].strip().strip("`") + command = cells[command_index].strip().strip("`") if command and command not in {"-", "[command if applicable]"}: commands.append(command) return commands @@ -417,7 +446,6 @@ def _assert_archive_plan_acceptance( material = [pair for pair in validated if _handoff_has_material_changes(*pair)] for command in commands: terminal_results: set[str] = set() - other_results: set[str] = set() for handoff, _brief in validated: result = _handoff_command_result(handoff, command) if result is None: @@ -425,22 +453,14 @@ def _assert_archive_plan_acceptance( tree = _verified_handoff_tree(git_root, handoff) if terminal_tree and tree == terminal_tree: terminal_results.add(result) - else: - other_results.add(result) - judged = terminal_results or other_results if terminal_results: if terminal_results == {"passed"}: continue raise SystemExit( f"acceptance-blocked: declared plan-level acceptance {command} is {_acceptance_result_detail(terminal_results)}" ) - if judged == {"passed"}: - if len(material) <= 1: - continue - raise SystemExit(f"acceptance-blocked: declared plan-level acceptance {command} is stale") - raise SystemExit( - f"acceptance-blocked: declared plan-level acceptance {command} is {_acceptance_result_detail(judged)}" - ) + # Historical task evidence is not terminal plan authority. The archive + # gate obtains one fresh state-neutral observation below instead. workspace = git_root if material else _resolve_final_plan_workspace(args) for command in commands: _assert_archive_command_state_neutral(command, workspace) @@ -783,8 +803,7 @@ def cmd_archive_plan(args: argparse.Namespace) -> None: root_path = artifact_path_from_row(root_match, args) require_plan_reviews(project_root(args), root_path, source_root=_resolve_final_plan_workspace(args)) if _plan_uses_accepted_result_authority(args, args.id): - _accepted_plan_task_results(args, args.id) - validated: list[tuple[dict[str, object], dict[str, object]]] = [] + validated = _accepted_plan_task_results(args, args.id) else: # Pre-accepted-result plans retain a bounded migration path. New plans # switch irreversibly once any task publishes durable accepted authority. diff --git a/tests/test_wor109_lightweight.py b/tests/test_dev_task_plan_scope.py similarity index 100% rename from tests/test_wor109_lightweight.py rename to tests/test_dev_task_plan_scope.py diff --git a/tests/test_wor109_accepted_result.py b/tests/test_orchestration_accepted_result.py similarity index 84% rename from tests/test_wor109_accepted_result.py rename to tests/test_orchestration_accepted_result.py index 4fdc936..c731b4a 100644 --- a/tests/test_wor109_accepted_result.py +++ b/tests/test_orchestration_accepted_result.py @@ -8,6 +8,10 @@ ORCHESTRATION = Path(__file__).resolve().parents[1] / "scripts" / "orchestration" +loaded_core = sys.modules.get("core") +loaded_core_path = Path(getattr(loaded_core, "__file__", "")) if loaded_core is not None else None +if loaded_core_path is not None and ORCHESTRATION not in loaded_core_path.parents: + sys.modules.pop("core", None) sys.path.insert(0, str(ORCHESTRATION)) import execution_context # noqa: E402 @@ -36,6 +40,7 @@ "validation_evidence_ids", "review_id", "owner_identity", + "knowledge_disposition", "accepted_at", "invalidation", } @@ -109,6 +114,11 @@ def _handoff() -> dict[str, object]: def _validated() -> dict[str, object]: return { "result_state": "completed", + "knowledge_disposition": { + "action": "none", + "reason": "No durable authority changed.", + "affected_authority": [], + }, "task_ownership": { "delegated": True, "owner_kind": "subagent", @@ -191,13 +201,24 @@ def test_accepted_result_is_deterministic_current_authority_not_handoff_history( "baseline_identity": {"head": OID_A, "tree": OID_B}, "accepted_source": {"head": OID_A, "tree": OID_B}, "authority_projection": first["authority_projection"], + "knowledge_disposition": first["knowledge_disposition"], } ) assert first["validation_evidence_ids"] == ["obs-001"] + assert first["knowledge_disposition"] == _validated()["knowledge_disposition"] assert "mutation_events" not in repr(first) assert "validation" not in first["executor_result_digest"] execution_context.assert_accepted_task_result_current(task, appended, first) + tampered_disposition = deepcopy(first) + tampered_disposition["knowledge_disposition"] = { + "action": "update", + "reason": "Tampered after acceptance.", + "affected_authority": ["REQ-001"], + } + with pytest.raises(SystemExit, match="accepted task result.*source"): + execution_context.assert_accepted_task_result_current(task, binding, tampered_disposition) + changed_scope = deepcopy(task) changed_scope["files"]["write"] = ["src/other.py"] with pytest.raises(SystemExit, match="accepted task result.*scope"): @@ -224,6 +245,34 @@ def test_accepted_result_is_deterministic_current_authority_not_handoff_history( execution_context.assert_accepted_task_result_current(task, binding, invalidated) +def test_legacy_accepted_result_without_disposition_remains_current_for_nonknowledge_consumers( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + task = _task(tmp_path) + binding = _binding(tmp_path) + monkeypatch.setattr( + execution_context, + "capture_repository_evidence", + lambda _root: {"head": OID_A, "tree": OID_B, "entries": {}, "status": "clean"}, + ) + accepted = execution_context.build_accepted_task_result( + task, binding, _handoff(), _validated(), accepted_at="2026-09-06T10:00:00Z" + ) + legacy = deepcopy(accepted) + legacy.pop("knowledge_disposition") + legacy["accepted_source"]["state_digest"] = execution_context._accepted_source_state_digest( + plan_id=legacy["plan_id"], + task_id=legacy["task_id"], + binding_id=legacy["binding_id"], + baseline_identity=legacy["baseline_identity"], + head=legacy["accepted_source"]["head"], + tree=legacy["accepted_source"]["tree"], + authority_projection=legacy["authority_projection"], + ) + + execution_context.assert_accepted_task_result_current(task, binding, legacy) + + def test_actual_accepted_repair_review_mode_and_frontier_are_digest_authority( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: diff --git a/tests/test_wor109_lifecycle.py b/tests/test_orchestration_accepted_result_lifecycle.py similarity index 55% rename from tests/test_wor109_lifecycle.py rename to tests/test_orchestration_accepted_result_lifecycle.py index c0a8650..ec4a1e4 100644 --- a/tests/test_wor109_lifecycle.py +++ b/tests/test_orchestration_accepted_result_lifecycle.py @@ -1,6 +1,7 @@ from __future__ import annotations import argparse +from copy import deepcopy import importlib.util import json from pathlib import Path @@ -15,17 +16,29 @@ import execution_context # noqa: E402 import plans # noqa: E402 -from test_wor109_accepted_result import _binding, _handoff, _task, _validated # noqa: E402 +from test_orchestration_accepted_result import _binding, _handoff, _task, _validated # noqa: E402 def _dispatcher(): - spec = importlib.util.spec_from_file_location( - "wor109_dispatcher", ORCHESTRATION / "dispatcher.py" - ) - assert spec is not None and spec.loader is not None - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - return module + previous_core = sys.modules.get("core") + core_spec = importlib.util.spec_from_file_location("core", ORCHESTRATION / "core.py") + assert core_spec is not None and core_spec.loader is not None + core_module = importlib.util.module_from_spec(core_spec) + sys.modules["core"] = core_module + try: + core_spec.loader.exec_module(core_module) + spec = importlib.util.spec_from_file_location( + "orchestration_accepted_result_dispatcher", ORCHESTRATION / "dispatcher.py" + ) + assert spec is not None and spec.loader is not None + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + finally: + if previous_core is None: + sys.modules.pop("core", None) + else: + sys.modules["core"] = previous_core def test_current_accepted_result_does_not_read_handoff_or_replay_validation( @@ -174,27 +187,140 @@ def test_archive_switches_irreversibly_to_accepted_results_without_handoff_repla json.dumps({"accepted_result": {"schema": "accepted-task-result-v1"}}), encoding="utf-8", ) - calls: list[str] = [] + accepted = [ + ( + { + "schema": "accepted-task-result-v1", + "task_id": "task-001", + "knowledge_disposition": { + "action": "none", + "reason": "No durable authority changed.", + "affected_authority": [], + }, + }, + {"task_id": "task-001", "review_required": False}, + ) + ] + calls: list[object] = [] monkeypatch.setattr(plans, "require_plan_reviews", lambda *_args, **_kwargs: None) monkeypatch.setattr( plans, "_accepted_plan_task_results", - lambda *_args: calls.append("accepted") or [], + lambda *_args: calls.append("accepted") or accepted, ) monkeypatch.setattr( plans, "_validated_plan_task_handoffs", lambda *_args: (_ for _ in ()).throw(AssertionError("handoff replayed")), ) - monkeypatch.setattr(plans, "_assert_archive_knowledge_gate", lambda *_args: None) - monkeypatch.setattr(plans, "_assert_archive_plan_acceptance", lambda *_args: None) + monkeypatch.setattr( + plans, "_assert_archive_knowledge_gate", lambda *_args: calls.append(_args[-1]) + ) + monkeypatch.setattr( + plans, "_assert_archive_plan_acceptance", lambda *_args: calls.append(_args[-1]) + ) plans.cmd_archive_plan(argparse.Namespace(project_root=str(tmp_path), id="plan-001")) - assert calls == ["accepted"] + assert calls == ["accepted", accepted, accepted] assert (tmp_path / ".work-bundle/orchestration/plan/archived/plan.md").is_file() +def test_archive_knowledge_gate_aggregates_new_results_and_bounds_legacy_bridge( + tmp_path: Path, +) -> None: + plan = tmp_path / "plan.md" + args = argparse.Namespace() + update = { + "schema": "accepted-task-result-v1", + "task_id": "task-001", + "knowledge_disposition": { + "action": "update", + "reason": "Stable authority changed.", + "affected_authority": ["REQ-001"], + }, + } + brief = {"task_id": "task-001", "review_required": True} + plan.write_text( + "---\nid: plan-001\n---\n\n## 2.1 Knowledge Base Update Carry Forward\n\n" + "- **Disposition**: not-needed\n- **Closure return**: missing\n", + encoding="utf-8", + ) + with pytest.raises(SystemExit, match="task-001:update"): + plans._assert_archive_knowledge_gate(args, "plan-001", plan, [(update, brief)]) + + plan.write_text( + plan.read_text(encoding="utf-8").replace( + "Closure return**: missing", "Closure return**: completed" + ), + encoding="utf-8", + ) + plans._assert_archive_knowledge_gate(args, "plan-001", plan, [(update, brief)]) + + legacy = deepcopy(update) + legacy.pop("knowledge_disposition") + with pytest.raises(SystemExit, match="legacy accepted results require plan-level required/completed"): + plans._assert_archive_knowledge_gate(args, "plan-001", plan, [(legacy, brief)]) + plan.write_text( + plan.read_text(encoding="utf-8").replace( + "Disposition**: not-needed", "Disposition**: required" + ), + encoding="utf-8", + ) + plans._assert_archive_knowledge_gate(args, "plan-001", plan, [(legacy, brief)]) + + +def test_declared_integration_commands_follow_table_headers() -> None: + five_columns = ( + "## 7. Tests\n\n" + "| ID | Test Type | Target | Command | Expected Result |\n" + "|---|---|---|---|---|\n" + "| TEST-001 | integration | archive | `env true` | passed |\n" + ) + reordered = ( + "## Tests\n\n" + "| Command | Expected Result | Target | Test Type | ID | Can Run With |\n" + "|---|---|---|---|---|---|\n" + "| `python -m pytest -q` | passed | archive | integration | TEST-002 | - |\n" + ) + + assert plans._declared_integration_commands(five_columns) == ["env true"] + assert plans._declared_integration_commands(reordered) == ["python -m pytest -q"] + + +def test_missing_terminal_plan_proof_executes_once_state_neutrally( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + subprocess = __import__("subprocess") + subprocess.run(["git", "init", "-q", "-b", "main"], cwd=tmp_path, check=True) + subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=tmp_path, check=True) + subprocess.run(["git", "config", "user.name", "Test"], cwd=tmp_path, check=True) + (tmp_path / "tracked.txt").write_text("stable\n", encoding="utf-8") + subprocess.run(["git", "add", "."], cwd=tmp_path, check=True) + subprocess.run(["git", "commit", "-qm", "base"], cwd=tmp_path, check=True) + plan = tmp_path / "plan.md" + plan.write_text( + "---\nid: plan-001\n---\n\n## 7. Tests\n\n" + "| ID | Test Type | Target | Command | Expected Result |\n" + "|---|---|---|---|---|\n" + "| TEST-001 | integration | archive | `env true` | passed |\n", + encoding="utf-8", + ) + observed: list[str] = [] + monkeypatch.setattr(plans, "_material_repository_root", lambda *_args: tmp_path) + monkeypatch.setattr( + plans, + "_observe_archive_command", + lambda command, _workspace: observed.append(command) or "passed", + ) + + plans._assert_archive_plan_acceptance( + argparse.Namespace(project_root=str(tmp_path)), "plan-001", plan, [] + ) + + assert observed == ["env true"] + + def test_recovery_commands_are_not_public_dispatcher_actions() -> None: dispatcher = _dispatcher() diff --git a/tests/test_wor109_planner_runtime.py b/tests/test_orchestration_plan_return.py similarity index 100% rename from tests/test_wor109_planner_runtime.py rename to tests/test_orchestration_plan_return.py diff --git a/tests/test_wor109_planner_contracts.py b/tests/test_orchestration_planning_contracts.py similarity index 100% rename from tests/test_wor109_planner_contracts.py rename to tests/test_orchestration_planning_contracts.py diff --git a/tests/test_wor109_planner_scenarios.py b/tests/test_orchestration_planning_scenarios.py similarity index 99% rename from tests/test_wor109_planner_scenarios.py rename to tests/test_orchestration_planning_scenarios.py index 9ee0b64..1b64c31 100644 --- a/tests/test_wor109_planner_scenarios.py +++ b/tests/test_orchestration_planning_scenarios.py @@ -20,7 +20,7 @@ route_review_verdict, ) import execution_context # noqa: E402 -from test_wor109_accepted_result import _binding, _handoff, _task, _validated # noqa: E402 +from test_orchestration_accepted_result import _binding, _handoff, _task, _validated # noqa: E402 sys.path.insert(0, str(WORK_BUNDLE)) from stage_events import derive_planning_economics, validate_stage_event # noqa: E402 diff --git a/tests/test_orchestration_workflow_contracts.py b/tests/test_orchestration_workflow_contracts.py index 76bb558..c5bc146 100644 --- a/tests/test_orchestration_workflow_contracts.py +++ b/tests/test_orchestration_workflow_contracts.py @@ -1,6 +1,7 @@ from __future__ import annotations import argparse +import importlib.util import json from pathlib import Path import subprocess @@ -37,6 +38,28 @@ def read(path: str) -> str: return (REPO_ROOT / path).read_text(encoding="utf-8") +def load_orchestration_dispatcher(): + previous_core = sys.modules.get("core") + core_spec = importlib.util.spec_from_file_location("core", ORCH_ROOT / "core.py") + assert core_spec is not None and core_spec.loader is not None + core_module = importlib.util.module_from_spec(core_spec) + sys.modules["core"] = core_module + try: + core_spec.loader.exec_module(core_module) + dispatcher_spec = importlib.util.spec_from_file_location( + "orchestration_workflow_contracts_dispatcher", ORCH_ROOT / "dispatcher.py" + ) + assert dispatcher_spec is not None and dispatcher_spec.loader is not None + dispatcher = importlib.util.module_from_spec(dispatcher_spec) + dispatcher_spec.loader.exec_module(dispatcher) + return dispatcher + finally: + if previous_core is None: + sys.modules.pop("core", None) + else: + sys.modules["core"] = previous_core + + def evals() -> list[dict[str, object]]: return json.loads(read("references/evals/orchestration/evals.json"))["evals"] @@ -489,9 +512,7 @@ def test_archive_plan_completed_handoff_rejects_missing_harness_mutation_evidenc def test_archive_plan_cli_accepts_explicit_harness_mutation_evidence() -> None: - from dispatcher import build_parser - - parsed = build_parser().parse_args( + parsed = load_orchestration_dispatcher().build_parser().parse_args( [ "archive-plan", "--id", @@ -1248,7 +1269,7 @@ def test_archive_plan_accepts_mapped_invariant_handoff_with_harness_observation( assert (root / ".work-bundle/orchestration/plan/archived/compiler-plan.md").is_file() -def test_archive_plan_forwards_execution_binding_into_task_revalidation(tmp_path: Path) -> None: +def test_archive_plan_does_not_replay_task_validation_from_execution_binding(tmp_path: Path) -> None: from plans import cmd_archive_plan root, binding, command = _mapped_archive_workspace(tmp_path) @@ -1268,18 +1289,17 @@ def test_archive_plan_forwards_execution_binding_into_task_revalidation(tmp_path restored = tmp_path / "restored-mapped" restored.mkdir() restored_root, restored_binding, _command = _mapped_archive_workspace(restored) - with pytest.raises(SystemExit, match=rf"acceptance-blocked: declared plan-level acceptance {command} is missing"): - cmd_archive_plan( - archive_args( - restored_root, - "plan-001", - workspace_id="wrong-workspace", - execution_id=restored_binding["execution_id"], - repository_id=restored_binding["repository_id"], - execution_runtime_root=restored_binding["runtime_root"], - ) + cmd_archive_plan( + archive_args( + restored_root, + "plan-001", + workspace_id="wrong-workspace", + execution_id=restored_binding["execution_id"], + repository_id=restored_binding["repository_id"], + execution_runtime_root=restored_binding["runtime_root"], ) - assert (restored_root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() + ) + assert (restored_root / ".work-bundle/orchestration/plan/archived/compiler-plan.md").is_file() def test_passing_declared_plan_acceptance_allows_archive_without_second_reviewer(tmp_path: Path) -> None: @@ -1498,7 +1518,7 @@ def test_stale_plan_acceptance_after_later_material_task_blocks_archive(tmp_path actual_commit=later, ) - with pytest.raises(SystemExit, match="acceptance-blocked:.*is stale"): + with pytest.raises(SystemExit, match="acceptance-blocked:.*is failed"): cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() @@ -1604,7 +1624,7 @@ def test_same_day_out_of_id_order_stale_plan_acceptance_blocks_archive(tmp_path: actual_commit=later, ) - with pytest.raises(SystemExit, match="acceptance-blocked:.*is stale"): + with pytest.raises(SystemExit, match="acceptance-blocked:.*is failed"): cmd_archive_plan(archive_args(root, "plan-001")) assert (root / ".work-bundle/orchestration/plan/active/compiler-plan.md").is_file() diff --git a/tests/test_wor109_closure.py b/tests/test_wor109_closure.py deleted file mode 100644 index 1897a9e..0000000 --- a/tests/test_wor109_closure.py +++ /dev/null @@ -1,52 +0,0 @@ -from __future__ import annotations - -import copy -import importlib.util -import json -from pathlib import Path - -import pytest - -ROOT = Path(__file__).resolve().parents[1] -EVAL = ROOT / "evals" / "wor109" -spec = importlib.util.spec_from_file_location("wor109_verify", EVAL / "verify.py") -assert spec and spec.loader -verifier = importlib.util.module_from_spec(spec) -spec.loader.exec_module(verifier) - -def test_wor109_closure_has_exact_fixture_and_manifest_identity() -> None: - assert verifier.verify() == {"evaluation_id": "wor109-review-stable-orchestration-v1", "fixtures": 14, "changed_surfaces": 35, "verdict": "accepted"} - -def test_wor109_fixture_registry_is_closed_and_unique() -> None: - payload = json.loads((EVAL / "fixtures.json").read_text(encoding="utf-8")) - assert tuple(row["id"] for row in payload["fixtures"]) == tuple(f"PD-{i:02d}" for i in range(1, 15)) - assert len({row["pytest_node"] for row in payload["fixtures"]}) == 14 - -def test_wor109_tampered_fixture_is_rejected(tmp_path: Path) -> None: - payload = json.loads((EVAL / "fixtures.json").read_text(encoding="utf-8")) - payload["fixtures"] = payload["fixtures"][:-1] - path = tmp_path / "fixtures.json" - path.write_text(json.dumps(payload), encoding="utf-8") - with pytest.raises(verifier.VerificationError, match="exactly PD-01..PD-14"): - verifier.verify(fixtures_path=path) - - -def test_wor109_valid_existing_node_semantic_substitution_is_rejected(tmp_path: Path) -> None: - payload = json.loads((EVAL / "fixtures.json").read_text(encoding="utf-8")) - first = payload["fixtures"][0] - second = payload["fixtures"][1] - first["pytest_node"], second["pytest_node"] = second["pytest_node"], first["pytest_node"] - path = tmp_path / "fixtures.json" - path.write_text(json.dumps(payload), encoding="utf-8") - - with pytest.raises(verifier.VerificationError, match="semantic oracle binding mismatch"): - verifier.verify(fixtures_path=path) - - -def test_wor109_tampered_manifest_is_rejected(tmp_path: Path) -> None: - payload = copy.deepcopy(json.loads((EVAL / "migration-impact.json").read_text(encoding="utf-8"))) - payload["exclusions"]["WOR-107"]["authorized"] = True - path = tmp_path / "migration-impact.json" - path.write_text(json.dumps(payload), encoding="utf-8") - with pytest.raises(verifier.VerificationError, match="exclusion authorization"): - verifier.verify(migration_path=path) From e4c2976bdc88d7d9c86aefbc816d6da96a93e585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 10:31:32 +0800 Subject: [PATCH 24/48] fix(orchestration): stabilize plan review admission --- scripts/orchestration/execution_context.py | 166 ++++++++++++++++++ scripts/orchestration/review_runtime.py | 108 +++++++++--- ...st_orchestration_semantic_plan_identity.py | 90 ++++++++++ ...est_orchestration_static_task_admission.py | 94 ++++++++++ 4 files changed, 438 insertions(+), 20 deletions(-) create mode 100644 tests/test_orchestration_semantic_plan_identity.py create mode 100644 tests/test_orchestration_static_task_admission.py diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index 7a7353d..cf673b7 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -4055,6 +4055,172 @@ def _contains_resolved_source_record(value: Any, record: str) -> bool: return False +STATIC_TASK_FIELDS = frozenset( + { + "id", + "plan_id", + "phase_id", + "name", + "goal", + "status", + "order", + "task_type", + "date_created", + "last_updated", + "updated_at", + "owner", + "depends_on", + "source_ids", + "truth_basis", + "source_files", + "target_files", + "forbidden_files", + "target_symbols", + "interfaces", + "completion_criteria", + "methodology", + "executor_profile", + "acceptance_review", + "allocated_rules", + "allocated_skills", + "validation", + "evidence_reuse", + "evidence_capability", + "files", + "project_metadata_required", + "metadata_preflight", + "repository_id", + "repository_target", + "repository_preflight", + "accepted_repository_baseline", + "repository_baseline", + "dependency_paths", + "call_chains", + "path", + "accepted_result", + "accepted_results", + "accepted_result_reference", + "accepted_result_references", + "evidence_reference", + "evidence_references", + } +) + + +def _assert_static_task_fields(task: dict[str, Any], task_path: Path) -> None: + unsupported = sorted(set(task) - STATIC_TASK_FIELDS) + if unsupported: + raise SystemExit( + f"Task has unsupported static contract fields {', '.join(unsupported)}: {task_path}" + ) + + +def _assert_no_source_local_execution_artifacts(task: dict[str, Any], task_path: Path) -> None: + files = task.get("files") if isinstance(task.get("files"), dict) else {} + write_paths = _as_list(files.get("write")) or _as_list(task.get("target_files")) + for value in write_paths: + try: + path = canonical_relative_path(str(value)) + except OwnershipBlocker: + continue + if path == "orchestration/executions" or path.startswith("orchestration/executions/"): + raise SystemExit( + f"Task write scope uses a source-local execution artifact path: {task_path}: {path}" + ) + + +def static_task_brief(root: Path, task_path: Path) -> dict[str, Any]: + """Compile one task's static authority without runtime bindings or dependency results.""" + + task, _ = _read_structured(task_path) + _assert_static_task_fields(task, task_path) + _assert_no_source_local_execution_artifacts(task, task_path) + compile_args = argparse.Namespace( + project_root=str(root), + workspace_root=str(root), + task=str(task_path), + handoff=None, + base=None, + head=None, + ) + return _compile_task_brief(compile_args)[1]["task_brief"] + + +def static_plan_task_admission( + root: Path, plan_path: Path, *, content: str | None = None +) -> list[dict[str, Any]]: + """Compile and statically admit every task belonging to one executable plan.""" + + plan_root = root / ".work-bundle/orchestration/plan" + if not plan_path.resolve().is_relative_to(plan_root.resolve()): + raise SystemExit("plan review static-admission-blocked: root plan escapes plan store") + if content is None: + plan, _ = _read_structured(plan_path) + else: + if not content.startswith("---\n") or "\n---\n" not in content[4:]: + raise SystemExit("plan review static-admission-blocked: root plan lacks front matter") + plan = parse_yaml_subset(content[4:].split("\n---\n", 1)[0]) + if not isinstance(plan, dict): + raise SystemExit("plan review static-admission-blocked: root plan front matter is invalid") + plan_id = _artifact_id(plan, "id", plan_path) + task_paths: list[Path] = [] + for path in sorted(plan_root.rglob("*.md")): + if path == plan_path: + continue + data, _ = _read_structured(path) + if str(data.get("plan_id") or "") != plan_id or not data.get("phase_id"): + continue + task_paths.append(path) + compiled: list[dict[str, Any]] = [] + by_id: dict[str, Path] = {} + for task_path in task_paths: + try: + brief = static_task_brief(root, task_path) + except SystemExit as error: + raise SystemExit( + f"plan review static-admission-blocked: {task_path}: {error}" + ) from error + task_id = str(brief["task_id"]) + if task_id in by_id: + raise SystemExit( + f"plan review static-admission-blocked: duplicate task ID {task_id}: " + f"{by_id[task_id]} and {task_path}" + ) + by_id[task_id] = task_path + compiled.append(brief) + task_ids = set(by_id) + dependencies = { + str(brief["task_id"]): [str(value) for value in _as_list(brief.get("depends_on"))] + for brief in compiled + } + for task_id, required in dependencies.items(): + invalid = [value for value in required if value == task_id or value not in task_ids] + if invalid: + raise SystemExit( + f"plan review static-admission-blocked: {task_id} has impossible dependency " + f"{', '.join(invalid)}" + ) + visiting: set[str] = set() + visited: set[str] = set() + + def visit(task_id: str) -> None: + if task_id in visiting: + raise SystemExit( + f"plan review static-admission-blocked: dependency cycle includes {task_id}" + ) + if task_id in visited: + return + visiting.add(task_id) + for dependency in dependencies[task_id]: + visit(dependency) + visiting.remove(task_id) + visited.add(task_id) + + for task_id in dependencies: + visit(task_id) + return compiled + + def _compile_task_brief(args: argparse.Namespace) -> tuple[Path, dict[str, Any]]: root, task_path, task, task_body, records, source_paths = _task_context(args) task_id = _artifact_id(task, "id", task_path) diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index b6da5c7..b21a19b 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -442,6 +442,84 @@ def artifact_review_identity(path: Path, *, content: str | None = None) -> dict[ "sha256": hashlib.sha256(payload.encode()).hexdigest(), "source_tree": None} +PLAN_PROGRESS_FIELDS = frozenset( + { + "status", + "last_updated", + "updated_at", + "accepted_result", + "accepted_results", + "accepted_result_reference", + "accepted_result_references", + "evidence_reference", + "evidence_references", + "review_id", + "reviewed_head", + "target_identity", + "verdict", + "findings", + "review_mode", + "repair_frontier", + "review_reset", + } +) + + +def _semantic_plan_value(value: Any) -> Any: + if isinstance(value, dict): + return { + key: _semantic_plan_value(child) + for key, child in sorted(value.items()) + if key not in PLAN_PROGRESS_FIELDS + } + if isinstance(value, list): + return [_semantic_plan_value(child) for child in value] + return value + + +def _semantic_plan_artifact(path: Path, *, content: str | None = None) -> dict[str, Any]: + text = path.read_text(encoding="utf-8") if content is None else content.rstrip() + "\n" + if not text.startswith("---\n") or "\n---\n" not in text[4:]: + raise SystemExit(f"stage review: missing artifact front matter: {path}") + raw, body = text[4:].split("\n---\n", 1) + metadata = parse_yaml_subset(raw) + if not isinstance(metadata, dict) or not metadata.get("id"): + raise SystemExit(f"stage review: missing artifact identity: {path}") + return {"metadata": _semantic_plan_value(metadata), "body": body} + + +def semantic_plan_projection( + root: Path, plan_path: Path, *, content: str | None = None +) -> dict[str, Any]: + """Return the canonical executable projection used by plan review freshness.""" + + plan_root = root / ".work-bundle/orchestration/plan" + if not plan_path.resolve().is_relative_to(plan_root.resolve()): + raise SystemExit("stage review: root plan escapes plan store") + root_projection = _semantic_plan_artifact(plan_path, content=content) + plan_data = root_projection["metadata"] + plan_id = str(plan_data["id"]) + members = {str(plan_path.relative_to(plan_root)): root_projection} + for path in sorted(plan_root.rglob("*.md")): + if path == plan_path: + continue + if not path.resolve().is_relative_to(plan_root.resolve()): + raise SystemExit("stage review: plan member escapes plan store") + data, _ = _read_structured(path) + if str(data.get("plan_id", "")) != plan_id: + continue + members[str(path.relative_to(plan_root))] = _semantic_plan_artifact(path) + specifications = [ + artifact_review_identity(path) for path in _resolve_spec_paths(root, {}, plan_data) + ] + return { + "artifact_id": plan_id, + "revision": str(plan_data.get("version", "1")), + "members": members, + "specifications": specifications, + } + + def _require_current_review(root: Path, stage: str, identity: Mapping[str, Any]) -> None: """Read native records; historical prose is not an acceptance envelope.""" accepted = [] @@ -511,26 +589,14 @@ def require_specification_review(root: Path, path: Path, *, content: str | None def plan_review_identity(root: Path, plan_path: Path, *, content: str | None = None) -> dict[str, Any]: - plan_root = root / ".work-bundle/orchestration/plan" - if not plan_path.resolve().is_relative_to(plan_root.resolve()): - raise SystemExit("stage review: root plan escapes plan store") - identity = artifact_review_identity(plan_path, content=content) - members = {str(plan_path.relative_to(plan_root)): identity["sha256"]} - for path in sorted(plan_root.rglob("*.md")): - if path == plan_path: - continue - if not path.resolve().is_relative_to(plan_root.resolve()): - raise SystemExit("stage review: plan member escapes plan store") - data, _ = _read_structured(path) - if str(data.get("plan_id", "")) != identity["artifact_id"]: - continue - members[str(path.relative_to(plan_root))] = artifact_review_identity(path)["sha256"] - plan_data = (_read_structured(plan_path)[0] if content is None - else parse_yaml_subset(content.split("---", 2)[1])) - specifications = [artifact_review_identity(path) for path in _resolve_spec_paths(root, {}, plan_data)] - payload = {"members": members, "specifications": specifications} - identity["sha256"] = hashlib.sha256(json.dumps(payload, sort_keys=True).encode()).hexdigest() - return identity + projection = semantic_plan_projection(root, plan_path, content=content) + payload = json.dumps(projection, sort_keys=True, separators=(",", ":"), default=str) + return { + "artifact_id": projection["artifact_id"], + "revision": projection["revision"], + "sha256": hashlib.sha256(payload.encode()).hexdigest(), + "source_tree": None, + } def require_plan_reviews(root: Path, plan_path: Path, *, source_root: Path | None = None, @@ -540,6 +606,8 @@ def require_plan_reviews(root: Path, plan_path: Path, *, source_root: Path | Non for spec in _resolve_spec_paths(root, {}, data): require_specification_review(root, spec) identity = plan_review_identity(root, plan_path, content=content) + from execution_context import static_plan_task_admission + static_plan_task_admission(root, plan_path, content=content) _require_current_review(root, "plan", identity) if source_root is not None: def git(*args: str) -> str: diff --git a/tests/test_orchestration_semantic_plan_identity.py b/tests/test_orchestration_semantic_plan_identity.py new file mode 100644 index 0000000..ea93cd6 --- /dev/null +++ b/tests/test_orchestration_semantic_plan_identity.py @@ -0,0 +1,90 @@ +from __future__ import annotations + +import sys +from copy import deepcopy +from pathlib import Path + +import pytest + + +REPO_ROOT = Path(__file__).resolve().parents[1] +ORCHESTRATION = REPO_ROOT / "scripts" / "orchestration" +sys.path.insert(0, str(ORCHESTRATION)) + +import review_runtime # noqa: E402 + + +def _plan_graph(root: Path) -> tuple[Path, Path, Path]: + spec = root / ".work-bundle/orchestration/spec/active/spec-test.md" + plan_root = root / ".work-bundle/orchestration/plan/active" + plan = plan_root / "plan-test.md" + phase = plan_root / "plan-test/phase-001.md" + task = plan_root / "plan-test/phase-001/task-001.md" + task.parent.mkdir(parents=True) + spec.parent.mkdir(parents=True) + spec.write_text( + "---\nid: spec-test\nversion: 1\nstatus: verified\n---\n\n# Specification\n", + encoding="utf-8", + ) + plan.write_text( + "---\nid: plan-test\nversion: 1\nstatus: Planned\n" + "source_spec: [.work-bundle/orchestration/spec/active/spec-test.md]\n" + "---\n\n# Plan\n", + encoding="utf-8", + ) + phase.write_text( + "---\nid: phase-001\nplan_id: plan-test\nstatus: Planned\n" + "task_index:\n - {id: task-001, status: Planned}\n---\n\n# Phase\n", + encoding="utf-8", + ) + task.write_text( + "---\nid: task-001\nplan_id: plan-test\nphase_id: phase-001\n" + "status: Planned\ndepends_on: []\nsource_ids: [REQ-001]\n" + "target_files: [implementation.py]\nvalidation: [{id: VAL-001, kind: process}]\n" + "acceptance_review: {required: true}\n---\n\n# Task\n", + encoding="utf-8", + ) + return plan, phase, task + + +def test_progress_and_append_only_evidence_do_not_change_semantic_plan_identity(tmp_path: Path) -> None: + plan, phase, task = _plan_graph(tmp_path) + original = review_runtime.plan_review_identity(tmp_path, plan) + + plan.write_text(plan.read_text().replace("status: Planned", "status: In progress")) + phase.write_text( + phase.read_text() + .replace("status: Planned", "status: Completed") + .replace("---\n\n# Phase", "accepted_result_references: [result-phase-001]\n---\n\n# Phase") + ) + task.write_text( + task.read_text() + .replace("status: Planned", "status: Completed") + .replace( + "---\n\n# Task", + "accepted_result: result-task-001\nevidence_references: [VAL-001-observation]\n---\n\n# Task", + ) + ) + + assert review_runtime.plan_review_identity(tmp_path, plan) == original + + +@pytest.mark.parametrize( + ("field", "before", "after"), + [ + ("dependency", "depends_on: []", "depends_on: [task-000]"), + ("source authority", "source_ids: [REQ-001]", "source_ids: [REQ-002]"), + ("scope", "target_files: [implementation.py]", "target_files: [other.py]"), + ("validation", "id: VAL-001", "id: VAL-002"), + ("review allocation", "required: true", "required: false"), + ], +) +def test_executable_task_contract_changes_semantic_plan_identity( + tmp_path: Path, field: str, before: str, after: str +) -> None: + plan, _phase, task = _plan_graph(tmp_path) + original = deepcopy(review_runtime.plan_review_identity(tmp_path, plan)) + + task.write_text(task.read_text().replace(before, after), encoding="utf-8") + + assert review_runtime.plan_review_identity(tmp_path, plan) != original, field diff --git a/tests/test_orchestration_static_task_admission.py b/tests/test_orchestration_static_task_admission.py new file mode 100644 index 0000000..0fcc0c2 --- /dev/null +++ b/tests/test_orchestration_static_task_admission.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +import sys +from pathlib import Path + +import pytest + + +REPO_ROOT = Path(__file__).resolve().parents[1] +ORCHESTRATION = REPO_ROOT / "scripts" / "orchestration" +sys.path.insert(0, str(ORCHESTRATION)) + +import execution_context # noqa: E402 +import review_runtime # noqa: E402 +from test_orchestration_execution_context import workspace # noqa: E402 + + +def _plan(root: Path) -> Path: + return root / ".work-bundle/orchestration/plan/active/compiler-plan.md" + + +def test_static_plan_admission_compiles_every_task_without_runtime_state(tmp_path: Path) -> None: + root, _spec, first = workspace(tmp_path) + second = first.with_name("task-005.md") + second.write_text( + first.read_text() + .replace("id: task-004", "id: task-005") + .replace("phase_id: phase-001\n", "phase_id: phase-001\ndepends_on: [task-004]\n") + .replace("task_id: task-004", "task_id: task-005"), + encoding="utf-8", + ) + first.write_text( + first.read_text().replace( + "---\n\n# Task", + "accepted_result: result-task-004\nevidence_references: [VAL-004-observation]\n---\n\n# Task", + ), + encoding="utf-8", + ) + + admitted = execution_context.static_plan_task_admission(root, _plan(root)) + + assert [item["task_id"] for item in admitted] == ["task-004", "task-005"] + assert not (root / ".work-bundle/runtime").exists() + + +def test_static_plan_admission_rejects_missing_dependency(tmp_path: Path) -> None: + root, _spec, task = workspace(tmp_path) + task.write_text( + task.read_text().replace( + "phase_id: phase-001\n", "phase_id: phase-001\ndepends_on: [task-missing]\n" + ) + ) + + with pytest.raises(SystemExit, match="static-admission-blocked.*task-missing"): + execution_context.static_plan_task_admission(root, _plan(root)) + + +@pytest.mark.parametrize( + ("needle", "replacement", "message"), + [ + ("id: task-004", "id: task-004\nunsupported_contract: true", "unsupported"), + ( + "write: [scripts/orchestration/execution_context.py]", + "write: [orchestration/executions/plan-test/result.yaml]", + "execution artifact", + ), + ], +) +def test_static_plan_admission_rejects_known_static_contract_errors( + tmp_path: Path, needle: str, replacement: str, message: str +) -> None: + root, _spec, task = workspace(tmp_path) + task.write_text(task.read_text().replace(needle, replacement), encoding="utf-8") + + with pytest.raises(SystemExit, match=message): + execution_context.static_plan_task_admission(root, _plan(root)) + + +def test_plan_review_gate_runs_static_admission_before_acceptance(tmp_path: Path, monkeypatch) -> None: + root, _spec, task = workspace(tmp_path) + task.write_text( + task.read_text().replace( + "phase_id: phase-001\n", "phase_id: phase-001\ndepends_on: [task-missing]\n" + ) + ) + monkeypatch.setattr(review_runtime, "require_specification_review", lambda *_args, **_kwargs: None) + monkeypatch.setattr( + review_runtime, + "_require_current_review", + lambda *_args, **_kwargs: (_ for _ in ()).throw(AssertionError("review accepted before admission")), + ) + + with pytest.raises(SystemExit, match="static-admission-blocked"): + review_runtime.require_plan_reviews(root, _plan(root)) From fbff0543cf37e72b14ba9169af600cdc6af8c048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 10:54:08 +0800 Subject: [PATCH 25/48] fix(orchestration): preserve accepted plan identity --- scripts/orchestration/review_runtime.py | 68 ++++++++++++------- ...st_orchestration_semantic_plan_identity.py | 54 +++++++++++++-- 2 files changed, 93 insertions(+), 29 deletions(-) diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index b21a19b..53287eb 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -442,11 +442,8 @@ def artifact_review_identity(path: Path, *, content: str | None = None) -> dict[ "sha256": hashlib.sha256(payload.encode()).hexdigest(), "source_tree": None} -PLAN_PROGRESS_FIELDS = frozenset( +PLAN_APPEND_ONLY_FIELDS = frozenset( { - "status", - "last_updated", - "updated_at", "accepted_result", "accepted_results", "accepted_result_reference", @@ -454,10 +451,7 @@ def artifact_review_identity(path: Path, *, content: str | None = None) -> dict[ "evidence_reference", "evidence_references", "review_id", - "reviewed_head", "target_identity", - "verdict", - "findings", "review_mode", "repair_frontier", "review_reset", @@ -465,13 +459,28 @@ def artifact_review_identity(path: Path, *, content: str | None = None) -> dict[ ) -def _semantic_plan_value(value: Any) -> Any: +def _semantic_plan_value(value: Any, *, top_level: bool = False) -> Any: if isinstance(value, dict): - return { - key: _semantic_plan_value(child) - for key, child in sorted(value.items()) - if key not in PLAN_PROGRESS_FIELDS - } + projected: dict[str, Any] = {} + created = value.get("date_created") + for key, child in sorted(value.items()): + if key in PLAN_APPEND_ONLY_FIELDS: + continue + if top_level and key in {"status", "last_updated", "updated_at"}: + continue + if key == "status": + projected[key] = "Planned" + elif key in {"last_updated", "updated_at"} and created is not None: + projected[key] = _semantic_plan_value(created) + elif key == "verdict": + projected[key] = "pending" + elif key == "reviewed_head": + projected[key] = "" + elif key == "findings": + projected[key] = [] + else: + projected[key] = _semantic_plan_value(child) + return projected if isinstance(value, list): return [_semantic_plan_value(child) for child in value] return value @@ -485,7 +494,17 @@ def _semantic_plan_artifact(path: Path, *, content: str | None = None) -> dict[s metadata = parse_yaml_subset(raw) if not isinstance(metadata, dict) or not metadata.get("id"): raise SystemExit(f"stage review: missing artifact identity: {path}") - return {"metadata": _semantic_plan_value(metadata), "body": body} + return {"metadata": _semantic_plan_value(metadata, top_level=True), "body": body} + + +def _semantic_plan_artifact_digest(projection: Mapping[str, Any]) -> str: + payload = json.dumps( + [projection["metadata"], projection["body"]], + sort_keys=True, + default=str, + separators=(",", ":"), + ) + return hashlib.sha256(payload.encode()).hexdigest() def semantic_plan_projection( @@ -499,7 +518,9 @@ def semantic_plan_projection( root_projection = _semantic_plan_artifact(plan_path, content=content) plan_data = root_projection["metadata"] plan_id = str(plan_data["id"]) - members = {str(plan_path.relative_to(plan_root)): root_projection} + members = { + str(plan_path.relative_to(plan_root)): _semantic_plan_artifact_digest(root_projection) + } for path in sorted(plan_root.rglob("*.md")): if path == plan_path: continue @@ -508,13 +529,13 @@ def semantic_plan_projection( data, _ = _read_structured(path) if str(data.get("plan_id", "")) != plan_id: continue - members[str(path.relative_to(plan_root))] = _semantic_plan_artifact(path) + members[str(path.relative_to(plan_root))] = _semantic_plan_artifact_digest( + _semantic_plan_artifact(path) + ) specifications = [ artifact_review_identity(path) for path in _resolve_spec_paths(root, {}, plan_data) ] return { - "artifact_id": plan_id, - "revision": str(plan_data.get("version", "1")), "members": members, "specifications": specifications, } @@ -590,13 +611,10 @@ def require_specification_review(root: Path, path: Path, *, content: str | None def plan_review_identity(root: Path, plan_path: Path, *, content: str | None = None) -> dict[str, Any]: projection = semantic_plan_projection(root, plan_path, content=content) - payload = json.dumps(projection, sort_keys=True, separators=(",", ":"), default=str) - return { - "artifact_id": projection["artifact_id"], - "revision": projection["revision"], - "sha256": hashlib.sha256(payload.encode()).hexdigest(), - "source_tree": None, - } + identity = artifact_review_identity(plan_path, content=content) + payload = json.dumps(projection, sort_keys=True, default=str) + identity["sha256"] = hashlib.sha256(payload.encode()).hexdigest() + return identity def require_plan_reviews(root: Path, plan_path: Path, *, source_root: Path | None = None, diff --git a/tests/test_orchestration_semantic_plan_identity.py b/tests/test_orchestration_semantic_plan_identity.py index ea93cd6..30b04ba 100644 --- a/tests/test_orchestration_semantic_plan_identity.py +++ b/tests/test_orchestration_semantic_plan_identity.py @@ -1,5 +1,7 @@ from __future__ import annotations +import hashlib +import json import sys from copy import deepcopy from pathlib import Path @@ -12,6 +14,7 @@ sys.path.insert(0, str(ORCHESTRATION)) import review_runtime # noqa: E402 +from artifact_inputs import _read_structured, _resolve_spec_paths # noqa: E402 def _plan_graph(root: Path) -> tuple[Path, Path, Path]: @@ -27,39 +30,82 @@ def _plan_graph(root: Path) -> tuple[Path, Path, Path]: encoding="utf-8", ) plan.write_text( - "---\nid: plan-test\nversion: 1\nstatus: Planned\n" + "---\nid: plan-test\nversion: 1\ndate_created: 2026-09-08\n" + "last_updated: 2026-09-08\nstatus: Planned\n" "source_spec: [.work-bundle/orchestration/spec/active/spec-test.md]\n" "---\n\n# Plan\n", encoding="utf-8", ) phase.write_text( - "---\nid: phase-001\nplan_id: plan-test\nstatus: Planned\n" + "---\nid: phase-001\nplan_id: plan-test\ndate_created: 2026-09-08\n" + "last_updated: 2026-09-08\nstatus: Planned\n" "task_index:\n - {id: task-001, status: Planned}\n---\n\n# Phase\n", encoding="utf-8", ) task.write_text( "---\nid: task-001\nplan_id: plan-test\nphase_id: phase-001\n" + "date_created: 2026-09-08\nlast_updated: 2026-09-08\n" "status: Planned\ndepends_on: []\nsource_ids: [REQ-001]\n" "target_files: [implementation.py]\nvalidation: [{id: VAL-001, kind: process}]\n" - "acceptance_review: {required: true}\n---\n\n# Task\n", + "acceptance_review: {required: true, verdict: pending, reviewed_head: '', findings: []}\n" + "---\n\n# Task\n", encoding="utf-8", ) return plan, phase, task +def _legacy_plan_identity(root: Path, plan: Path) -> dict[str, object]: + plan_root = root / ".work-bundle/orchestration/plan" + identity = review_runtime.artifact_review_identity(plan) + members = {str(plan.relative_to(plan_root)): identity["sha256"]} + for path in sorted(plan_root.rglob("*.md")): + if path == plan: + continue + data, _ = _read_structured(path) + if str(data.get("plan_id", "")) == identity["artifact_id"]: + members[str(path.relative_to(plan_root))] = review_runtime.artifact_review_identity(path)[ + "sha256" + ] + plan_data = _read_structured(plan)[0] + specifications = [ + review_runtime.artifact_review_identity(path) + for path in _resolve_spec_paths(root, {}, plan_data) + ] + identity["sha256"] = hashlib.sha256( + json.dumps({"members": members, "specifications": specifications}, sort_keys=True).encode() + ).hexdigest() + return identity + + +def test_semantic_projector_preserves_accepted_legacy_baseline_identity(tmp_path: Path) -> None: + plan, _phase, _task = _plan_graph(tmp_path) + + assert review_runtime.plan_review_identity(tmp_path, plan) == _legacy_plan_identity(tmp_path, plan) + + def test_progress_and_append_only_evidence_do_not_change_semantic_plan_identity(tmp_path: Path) -> None: plan, phase, task = _plan_graph(tmp_path) original = review_runtime.plan_review_identity(tmp_path, plan) - plan.write_text(plan.read_text().replace("status: Planned", "status: In progress")) + plan.write_text( + plan.read_text() + .replace("status: Planned", "status: In progress") + .replace("last_updated: 2026-09-08", "last_updated: 2026-09-09") + ) phase.write_text( phase.read_text() .replace("status: Planned", "status: Completed") + .replace("last_updated: 2026-09-08", "last_updated: 2026-09-09") .replace("---\n\n# Phase", "accepted_result_references: [result-phase-001]\n---\n\n# Phase") ) task.write_text( task.read_text() .replace("status: Planned", "status: Completed") + .replace("last_updated: 2026-09-08", "last_updated: 2026-09-09") + .replace( + "acceptance_review: {required: true, verdict: pending, reviewed_head: '', findings: []}", + "acceptance_review: {required: true, verdict: accept, reviewed_head: abc, findings: []}", + ) .replace( "---\n\n# Task", "accepted_result: result-task-001\nevidence_references: [VAL-001-observation]\n---\n\n# Task", From 106f5766e471c1d56bd3b57bd0012d9144d8f7fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 11:05:07 +0800 Subject: [PATCH 26/48] fix(orchestration): ignore plan archive rotation --- scripts/orchestration/review_runtime.py | 13 +++++++++++-- .../test_orchestration_semantic_plan_identity.py | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index 53287eb..4919629 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -507,6 +507,13 @@ def _semantic_plan_artifact_digest(projection: Mapping[str, Any]) -> str: return hashlib.sha256(payload.encode()).hexdigest() +def _semantic_plan_member_key(plan_root: Path, path: Path) -> str: + parts = list(path.relative_to(plan_root).parts) + if parts and parts[0] == "archived": + parts[0] = "active" + return Path(*parts).as_posix() + + def semantic_plan_projection( root: Path, plan_path: Path, *, content: str | None = None ) -> dict[str, Any]: @@ -519,7 +526,9 @@ def semantic_plan_projection( plan_data = root_projection["metadata"] plan_id = str(plan_data["id"]) members = { - str(plan_path.relative_to(plan_root)): _semantic_plan_artifact_digest(root_projection) + _semantic_plan_member_key(plan_root, plan_path): _semantic_plan_artifact_digest( + root_projection + ) } for path in sorted(plan_root.rglob("*.md")): if path == plan_path: @@ -529,7 +538,7 @@ def semantic_plan_projection( data, _ = _read_structured(path) if str(data.get("plan_id", "")) != plan_id: continue - members[str(path.relative_to(plan_root))] = _semantic_plan_artifact_digest( + members[_semantic_plan_member_key(plan_root, path)] = _semantic_plan_artifact_digest( _semantic_plan_artifact(path) ) specifications = [ diff --git a/tests/test_orchestration_semantic_plan_identity.py b/tests/test_orchestration_semantic_plan_identity.py index 30b04ba..97f3f13 100644 --- a/tests/test_orchestration_semantic_plan_identity.py +++ b/tests/test_orchestration_semantic_plan_identity.py @@ -2,6 +2,7 @@ import hashlib import json +import shutil import sys from copy import deepcopy from pathlib import Path @@ -83,6 +84,20 @@ def test_semantic_projector_preserves_accepted_legacy_baseline_identity(tmp_path assert review_runtime.plan_review_identity(tmp_path, plan) == _legacy_plan_identity(tmp_path, plan) +def test_active_to_archived_rotation_preserves_semantic_plan_identity(tmp_path: Path) -> None: + plan, _phase, _task = _plan_graph(tmp_path) + original = review_runtime.plan_review_identity(tmp_path, plan) + archived = plan.parents[1] / "archived" + archived.mkdir() + archived_plan = archived / plan.name + archived_graph = archived / plan.stem + + shutil.move(str(plan), archived_plan) + shutil.move(str(plan.with_suffix("")), archived_graph) + + assert review_runtime.plan_review_identity(tmp_path, archived_plan) == original + + def test_progress_and_append_only_evidence_do_not_change_semantic_plan_identity(tmp_path: Path) -> None: plan, phase, task = _plan_graph(tmp_path) original = review_runtime.plan_review_identity(tmp_path, plan) From 7fe45c9a894bda31e83981a361ac2e51d5d9586b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 11:33:31 +0800 Subject: [PATCH 27/48] fix(orchestration): reuse terminal observations on archive --- scripts/orchestration/execution_context.py | 12 +- scripts/orchestration/plans.py | 100 ++++++- tests/test_orchestration_observation_reuse.py | 256 ++++++++++++++++++ 3 files changed, 366 insertions(+), 2 deletions(-) create mode 100644 tests/test_orchestration_observation_reuse.py diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index cf673b7..50bf43c 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -4016,7 +4016,17 @@ def _compile_task_validation( ) -> list[Any]: validation_items = _as_list(task.get("validation")) if validation_items: - return [_compile_structured_validation_item(item) for item in validation_items] + task_policy = task.get("evidence_reuse") + compiled: list[dict[str, Any]] = [] + for item in validation_items: + if task_policy is not None and isinstance(item, dict) and not ( + "evidence_reuse" in item or "reuse_seconds" in item + ): + item = {**item, "evidence_reuse": task_policy} + elif task_policy is not None and not isinstance(item, dict): + raise SystemExit("Task validation items must be mappings") + compiled.append(_compile_structured_validation_item(item)) + return compiled if _section_table(task_body, "Validation"): raise SystemExit( "Untyped Validation table row is legacy-untyped; migrate to front-matter validation with explicit kind" diff --git a/scripts/orchestration/plans.py b/scripts/orchestration/plans.py index 461fc75..31ab2d5 100644 --- a/scripts/orchestration/plans.py +++ b/scripts/orchestration/plans.py @@ -11,6 +11,7 @@ unique_explicit_handoff_plan_id, validate_executor_result_for_task, _compile_task_brief, + _observe_validation_item, _observation_kwargs, _parse_scalar, _execution_workspace_module, @@ -20,7 +21,13 @@ load_current_accepted_task_result, semantic_digest, ) -from completion_provenance import ManagedProvenanceStore, release_completion_binding +from completion_provenance import ( + CompletionProvenanceError, + ManagedProvenanceStore, + load_observation, + observe_validation, + release_completion_binding, +) from handoffs import _read_compact_yaml_metadata from repository_preflight import capture_repository_evidence, task_caused_paths from specs import load_index, replace_front_matter_value @@ -431,6 +438,82 @@ def _assert_archive_command_state_neutral(command: str, workspace: Path) -> None ) +def _observe_archive_obligations( + control_root: Path, + command: str, + workspace: Path, + validated: list[tuple[dict[str, object], dict[str, object]]], +) -> list[dict[str, object]]: + """Consume accepted task observations through the shared validation observer.""" + + store = ManagedProvenanceStore( + control_root / ".work-bundle/runtime/completion-provenance" + ) + matches: list[tuple[dict[str, object], dict[str, object], dict[str, object]]] = [] + for accepted, task in validated: + if accepted.get("schema") != "accepted-task-result-v1": + continue + for item in task.get("validation", []): + if isinstance(item, dict) and str(item.get("command") or "").strip() == command: + matches.append((accepted, task, item)) + if not matches: + return [] + + observed: list[dict[str, object]] = [] + for accepted, task, item in matches: + evidence_ids = accepted.get("validation_evidence_ids") + if not isinstance(evidence_ids, list) or not evidence_ids: + raise SystemExit( + "acceptance-blocked: accepted task result has no harness observation" + ) + definition = { + key: item.get(key) + for key in ( + "id", "kind", "command", "mechanism", "expected", + "acceptable_results", "invariant_ids", "digest", "proves", + ) + } + expected_command_digest = semantic_digest(definition) + capable = False + for evidence_id in evidence_ids: + try: + prior = load_observation(store, str(evidence_id)) + except CompletionProvenanceError: + continue + if prior.command_digest == expected_command_digest: + capable = True + break + if not capable: + raise SystemExit( + "acceptance-blocked: accepted task result does not reference an accepted harness observation" + ) + binding = load_task_execution_binding( + control_root, str(task.get("plan_id") or ""), str(task.get("task_id") or "") + ) + if Path(str(binding.get("execution_path") or "")).resolve() != workspace.resolve(): + raise SystemExit( + "acceptance-blocked: accepted observation execution workspace mismatch" + ) + try: + before = capture_repository_evidence(workspace) + except RuntimeError as error: + raise SystemExit(f"acceptance-blocked: {error}") from error + result = observe_validation( + binding, + task, + item, + before, + lambda receipt: _observe_validation_item(item, workspace, task, receipt), + lambda: capture_repository_evidence(workspace), + ) + if result.get("result") != "passed": + raise SystemExit( + f"acceptance-blocked: declared plan-level acceptance {command} is {result.get('result')}" + ) + observed.append(result) + return observed + + def _assert_archive_plan_acceptance( args: argparse.Namespace, plan_id: str, @@ -444,7 +527,20 @@ def _assert_archive_plan_acceptance( 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)] + uses_accepted_results = any( + result.get("schema") == "accepted-task-result-v1" for result, _brief in validated + ) + control_root = resolve_workspace_root(args) if uses_accepted_results else None for command in commands: + if control_root is not None: + observed = _observe_archive_obligations( + control_root, command, git_root, validated + ) + if observed: + continue + raise SystemExit( + f"acceptance-blocked: no accepted validation obligation for {command}" + ) terminal_results: set[str] = set() for handoff, _brief in validated: result = _handoff_command_result(handoff, command) @@ -461,6 +557,8 @@ def _assert_archive_plan_acceptance( ) # Historical task evidence is not terminal plan authority. The archive # gate obtains one fresh state-neutral observation below instead. + if control_root is not None: + return workspace = git_root if material else _resolve_final_plan_workspace(args) for command in commands: _assert_archive_command_state_neutral(command, workspace) diff --git a/tests/test_orchestration_observation_reuse.py b/tests/test_orchestration_observation_reuse.py new file mode 100644 index 0000000..b0da510 --- /dev/null +++ b/tests/test_orchestration_observation_reuse.py @@ -0,0 +1,256 @@ +from __future__ import annotations + +import json +import shlex +import subprocess +import sys +from copy import deepcopy +from pathlib import Path + +import pytest + + +REPO_ROOT = Path(__file__).resolve().parents[1] +ORCHESTRATION = REPO_ROOT / "scripts" / "orchestration" +sys.path.insert(0, str(ORCHESTRATION)) + +import completion_provenance # noqa: E402 +import execution_context # noqa: E402 +import plans # noqa: E402 +from repository_preflight import capture_repository_evidence # noqa: E402 + + +def _git(root: Path, *arguments: str) -> str: + completed = subprocess.run( + ["git", *arguments], cwd=root, check=True, capture_output=True, text=True + ) + return completed.stdout.strip() + + +def _fixture(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): + root = tmp_path / "repo" + root.mkdir() + _git(root, "init", "-q") + _git(root, "config", "user.email", "test@example.com") + _git(root, "config", "user.name", "Test") + source = root / "runtime.py" + source.write_text("VALUE = 1\n") + _git(root, "add", "runtime.py") + _git(root, "commit", "-qm", "baseline") + + control_root = tmp_path / "control" + control_root.mkdir() + counter = tmp_path / "executions.txt" + script = ( + "from pathlib import Path; " + f"p=Path({str(counter)!r}); " + "p.write_text(str(int(p.read_text()) + 1) if p.exists() else '1')" + ) + command = f"{shlex.quote(sys.executable)} -c {shlex.quote(script)}" + item = { + "id": "VAL-001", + "kind": "process", + "command": command, + "expected": "passed", + "invariant_ids": ["INV-001"], + "proves": ["REQ-001"], + "evidence_reuse": { + "mode": "deterministic", + "max_age_seconds": 3600, + "include_head": False, + }, + } + task = { + "plan_id": "plan-001", + "task_id": "task-001", + "source_ids": ["REQ-001"], + "requirements": ["REQ-001: reuse terminal evidence"], + "constraints": [], + "interfaces": {}, + "truth_basis": {"conflict_status": "clear"}, + "files": {"read": ["runtime.py"], "write": ["runtime.py"], "forbidden": []}, + "evidence_capability": {"result": "mapped"}, + "validation": [item], + } + binding = { + "control_root": str(control_root), + "execution_path": str(root), + "workspace_id": "workspace-001", + "execution_id": "execution-001", + "repository_id": "repository-001", + "plan_id": "plan-001", + "task_id": "task-001", + } + evidence = capture_repository_evidence(root) + observed = completion_provenance.observe_validation( + binding, + task, + item, + evidence, + lambda receipt: execution_context._observe_validation_item(item, root, task, receipt), + lambda: capture_repository_evidence(root), + ) + accepted = { + "schema": "accepted-task-result-v1", + "plan_id": "plan-001", + "task_id": "task-001", + "validation_evidence_ids": [observed["observation_id"]], + } + monkeypatch.setattr(plans, "load_task_execution_binding", lambda *_: binding) + return root, control_root, counter, accepted, task, command, observed + + +def _archive(control_root: Path, root: Path, command: str, accepted: dict, task: dict): + return plans._observe_archive_obligations( + control_root, command, root, [(accepted, task)] + ) + + +def test_task_level_reuse_policy_is_applied_to_each_validation_obligation(): + compiled = execution_context._compile_task_validation( + { + "validation": [ + {"id": "VAL-001", "kind": "process", "command": "true", "expected": "passed"} + ], + "evidence_reuse": { + "mode": "deterministic", + "max_age_seconds": 120, + "include_head": False, + }, + }, + "", + [], + {}, + ) + + assert compiled[0]["evidence_reuse"]["max_age_seconds"] == 120 + + +def test_completion_then_archive_reuses_one_current_terminal_observation(tmp_path, monkeypatch): + root, control, counter, accepted, task, command, first = _fixture(tmp_path, monkeypatch) + + observed = _archive(control, root, command, accepted, task) + + assert counter.read_text() == "1" + assert observed[0]["observation_id"] == first["observation_id"] + assert observed[0]["reuse_of"] == first["observation_id"] + + +@pytest.mark.parametrize("change", ["source", "claim", "epoch"]) +def test_archive_executes_once_for_each_exact_invalidation(tmp_path, monkeypatch, change): + root, control, counter, accepted, task, command, first = _fixture(tmp_path, monkeypatch) + changed = deepcopy(task) + if change == "source": + (root / "runtime.py").write_text("VALUE = 2\n") + elif change == "claim": + changed["requirements"] = ["REQ-001: changed semantic claim"] + else: + store = completion_provenance.ManagedProvenanceStore( + control / ".work-bundle/runtime/completion-provenance" + ) + completion_provenance.record_relevant_mutation(store, "accepted invalidation") + + observed = _archive(control, root, command, accepted, changed) + + assert counter.read_text() == "2" + assert observed[0]["observation_id"] != first["observation_id"] + assert observed[0]["reuse_of"] is None + + +def test_changed_validation_allocation_requires_fresh_accepted_observation(tmp_path, monkeypatch): + root, control, counter, accepted, task, command, first = _fixture(tmp_path, monkeypatch) + changed = deepcopy(task) + changed["validation"][0]["invariant_ids"] = ["INV-002"] + binding = plans.load_task_execution_binding(control, "plan-001", "task-001") + evidence = capture_repository_evidence(root) + replacement = completion_provenance.observe_validation( + binding, + changed, + changed["validation"][0], + evidence, + lambda receipt: execution_context._observe_validation_item( + changed["validation"][0], root, changed, receipt + ), + lambda: capture_repository_evidence(root), + ) + accepted["validation_evidence_ids"] = [replacement["observation_id"]] + + observed = _archive(control, root, command, accepted, changed) + + assert counter.read_text() == "2" + assert replacement["observation_id"] != first["observation_id"] + assert observed[0]["reuse_of"] == replacement["observation_id"] + + +def test_distinct_accepted_obligations_each_reuse_without_lifecycle_replay(tmp_path, monkeypatch): + root, control, counter, accepted, task, command, _ = _fixture(tmp_path, monkeypatch) + other = deepcopy(task) + other["task_id"] = "task-002" + other["validation"][0]["id"] = "VAL-002" + binding_two = { + "control_root": str(control), + "execution_path": str(root), + "workspace_id": "workspace-001", + "execution_id": "execution-001", + "repository_id": "repository-001", + "plan_id": "plan-001", + "task_id": "task-002", + } + evidence = capture_repository_evidence(root) + second = completion_provenance.observe_validation( + binding_two, + other, + other["validation"][0], + evidence, + lambda receipt: execution_context._observe_validation_item(other["validation"][0], root, other, receipt), + lambda: capture_repository_evidence(root), + ) + accepted_two = { + "schema": "accepted-task-result-v1", + "plan_id": "plan-001", + "task_id": "task-002", + "validation_evidence_ids": [second["observation_id"]], + } + monkeypatch.setattr( + plans, + "load_task_execution_binding", + lambda _root, _plan, task_id: binding_two if task_id == "task-002" else { + **binding_two, "task_id": "task-001" + }, + ) + + observed = _archive(control, root, command, accepted, task) + observed += _archive(control, root, command, accepted_two, other) + + assert counter.read_text() == "2" + assert {item["reuse_of"] for item in observed} == { + accepted["validation_evidence_ids"][0], + accepted_two["validation_evidence_ids"][0], + } + + +def test_executor_authored_receipt_id_is_not_independent_archive_proof(tmp_path, monkeypatch): + root, control, counter, accepted, task, command, _ = _fixture(tmp_path, monkeypatch) + accepted["validation_evidence_ids"] = ["executor-forged-observation"] + + with pytest.raises(SystemExit, match="accepted harness observation"): + _archive(control, root, command, accepted, task) + + assert counter.read_text() == "1" + + +def test_archive_acceptance_uses_observation_contract_instead_of_direct_runner(tmp_path, monkeypatch): + root, control, counter, accepted, task, command, _ = _fixture(tmp_path, monkeypatch) + plan = tmp_path / "plan.md" + plan.write_text( + "## Tests\n\n| Test Type | Command |\n| --- | --- |\n" + f"| Integration | `{command}` |\n" + ) + monkeypatch.setattr(plans, "_material_repository_root", lambda *_: root) + monkeypatch.setattr(plans, "resolve_workspace_root", lambda *_: control) + monkeypatch.setattr(plans, "_assert_archive_command_state_neutral", lambda *_: pytest.fail("direct replay")) + args = type("Args", (), {"project_root": str(control), "workspace_root": str(control)})() + + plans._assert_archive_plan_acceptance(args, "plan-001", plan, [(accepted, task)]) + + assert counter.read_text() == "1" From 1b9476430c03c5d61d521e4d651c4ead6dbcee5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 11:46:22 +0800 Subject: [PATCH 28/48] fix(orchestration): persist archive observation reuse --- .../orchestration/completion_provenance.py | 31 ++++++++ scripts/orchestration/plans.py | 42 ++++++++-- tests/test_orchestration_observation_reuse.py | 79 ++++++++++++++----- 3 files changed, 126 insertions(+), 26 deletions(-) diff --git a/scripts/orchestration/completion_provenance.py b/scripts/orchestration/completion_provenance.py index 5674aed..93fe37a 100644 --- a/scripts/orchestration/completion_provenance.py +++ b/scripts/orchestration/completion_provenance.py @@ -634,6 +634,10 @@ def observe_validation( binding: Mapping[str, Any], task: Mapping[str, Any], item: Mapping[str, Any], evidence: Mapping[str, Any], execute: Callable[[dict[str, Any]], dict[str, Any]], capture: Callable[[], Mapping[str, Any]], + *, + finalization_id: str | None = None, + stage_event_workspace: str | Path | None = None, + stage_event: Mapping[str, Any] | None = None, ) -> dict[str, Any]: """Project validation onto the existing source/evidence and observation identities.""" from evaluation_identity import EvaluationIdentityError, validation_source_identity @@ -705,6 +709,14 @@ def run(): return observed if source_identity() != source or validation_environment_identity(root, policy) != environment: raise SystemExit("validation-blocked: inputs changed while obtaining evidence") + if finalization_id is not None: + record = _claim_reused_observation( + store, + record, + finalization_id=finalization_id, + stage_event_workspace=stage_event_workspace, + stage_event=stage_event, + ) if observed is None: observed = {key: item.get(key) for key in ("command", "kind", "id", "invariant_ids")} if item.get("kind") == "inspection": @@ -726,6 +738,25 @@ def claim_observation_identity( """Reuse or execute one exact observation and bind it to one finalization.""" observation = reuse_observation(store, request, execute, now=now) + return _claim_reused_observation( + store, + observation, + finalization_id=finalization_id, + stage_event_workspace=stage_event_workspace, + stage_event=stage_event, + ) + + +def _claim_reused_observation( + store: ManagedProvenanceStore, + observation: ObservationIdentityV1, + *, + finalization_id: str, + stage_event_workspace: str | Path | None = None, + stage_event: Mapping[str, Any] | None = None, +) -> ObservationIdentityV1: + """Persist one downstream consumer and its reuse event for an observation.""" + consume_observation(store, observation.observation_id, finalization_id) claimed = load_observation(store, observation.observation_id) if observation.reuse_of is not None: diff --git a/scripts/orchestration/plans.py b/scripts/orchestration/plans.py index 31ab2d5..2295a6a 100644 --- a/scripts/orchestration/plans.py +++ b/scripts/orchestration/plans.py @@ -474,16 +474,15 @@ def _observe_archive_obligations( ) } expected_command_digest = semantic_digest(definition) - capable = False + accepted_harness_observation = False for evidence_id in evidence_ids: try: - prior = load_observation(store, str(evidence_id)) + load_observation(store, str(evidence_id)) except CompletionProvenanceError: continue - if prior.command_digest == expected_command_digest: - capable = True - break - if not capable: + accepted_harness_observation = True + break + if not accepted_harness_observation: raise SystemExit( "acceptance-blocked: accepted task result does not reference an accepted harness observation" ) @@ -505,6 +504,37 @@ def _observe_archive_obligations( before, lambda receipt: _observe_validation_item(item, workspace, task, receipt), lambda: capture_repository_evidence(workspace), + finalization_id=( + f"archive:{task.get('plan_id')}:{task.get('task_id')}:{item.get('id')}" + ), + stage_event_workspace=control_root, + stage_event={ + "event_id": "event-template", + "timestamp": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), + "process_id": "process-plan-archive", + "stage": "plan-archive", + "attempt_id": str(task.get("plan_id") or ""), + "event_type": "suite_started", + "enforcement_mode": "native", + "join_ids": { + "specification_id": None, + "plan_id": str(task.get("plan_id") or "") or None, + "phase_id": str(task.get("phase_id") or "") or None, + "task_id": str(task.get("task_id") or "") or None, + "review_id": None, + "evaluation_id": None, + }, + "clocks": {"wall_ms": 0, "active_ms": 0, "billed_ms": None}, + "finding_class": None, + "return_reason": "accepted terminal observation", + "owner": str(task.get("task_id") or "plan-archive"), + "identity": { + "product_tree": _git_tree_id(workspace, "HEAD"), + "artifact_digest": expected_command_digest, + "mutation_epoch": 0, + }, + "privacy": "operational_metadata_only", + }, ) if result.get("result") != "passed": raise SystemExit( diff --git a/tests/test_orchestration_observation_reuse.py b/tests/test_orchestration_observation_reuse.py index b0da510..55fcdc9 100644 --- a/tests/test_orchestration_observation_reuse.py +++ b/tests/test_orchestration_observation_reuse.py @@ -106,6 +106,30 @@ def _archive(control_root: Path, root: Path, command: str, accepted: dict, task: ) +def _public_archive( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + control_root: Path, + root: Path, + command: str, + accepted: dict, + task: dict, +): + plan = tmp_path / "plan.md" + plan.write_text( + "## Tests\n\n| Test Type | Command |\n| --- | --- |\n" + f"| Integration | `{command}` |\n" + ) + monkeypatch.setattr(plans, "_material_repository_root", lambda *_: root) + monkeypatch.setattr(plans, "resolve_workspace_root", lambda *_: control_root) + plans._assert_archive_plan_acceptance( + type("Args", (), {"project_root": str(control_root), "workspace_root": str(control_root)})(), + "plan-001", + plan, + [(accepted, task)], + ) + + def test_task_level_reuse_policy_is_applied_to_each_validation_obligation(): compiled = execution_context._compile_task_validation( { @@ -129,11 +153,17 @@ def test_task_level_reuse_policy_is_applied_to_each_validation_obligation(): def test_completion_then_archive_reuses_one_current_terminal_observation(tmp_path, monkeypatch): root, control, counter, accepted, task, command, first = _fixture(tmp_path, monkeypatch) - observed = _archive(control, root, command, accepted, task) + _public_archive(tmp_path, monkeypatch, control, root, command, accepted, task) assert counter.read_text() == "1" - assert observed[0]["observation_id"] == first["observation_id"] - assert observed[0]["reuse_of"] == first["observation_id"] + state = json.loads( + (control / ".work-bundle/runtime/completion-provenance/completion-provenance-v1.json").read_text() + ) + assert state["consumptions"][first["observation_id"]] == ( + "archive:plan-001:task-001:VAL-001" + ) + events = completion_provenance._stage_events_module().query_stage_events(control) + assert [event.event_type for event in events] == ["suite_reused"] @pytest.mark.parametrize("change", ["source", "claim", "epoch"]) @@ -157,29 +187,38 @@ def test_archive_executes_once_for_each_exact_invalidation(tmp_path, monkeypatch assert observed[0]["reuse_of"] is None -def test_changed_validation_allocation_requires_fresh_accepted_observation(tmp_path, monkeypatch): +def test_changed_validation_allocation_executes_fresh_once_at_public_archive(tmp_path, monkeypatch): root, control, counter, accepted, task, command, first = _fixture(tmp_path, monkeypatch) changed = deepcopy(task) changed["validation"][0]["invariant_ids"] = ["INV-002"] - binding = plans.load_task_execution_binding(control, "plan-001", "task-001") - evidence = capture_repository_evidence(root) - replacement = completion_provenance.observe_validation( - binding, - changed, - changed["validation"][0], - evidence, - lambda receipt: execution_context._observe_validation_item( - changed["validation"][0], root, changed, receipt - ), - lambda: capture_repository_evidence(root), - ) - accepted["validation_evidence_ids"] = [replacement["observation_id"]] - observed = _archive(control, root, command, accepted, changed) + _public_archive(tmp_path, monkeypatch, control, root, command, accepted, changed) assert counter.read_text() == "2" + state = json.loads( + (control / ".work-bundle/runtime/completion-provenance/completion-provenance-v1.json").read_text() + ) + assert len(state["observations"]) == 2 + replacement = state["observations"][-1] assert replacement["observation_id"] != first["observation_id"] - assert observed[0]["reuse_of"] == replacement["observation_id"] + assert state["consumptions"][replacement["observation_id"]] == ( + "archive:plan-001:task-001:VAL-001" + ) + + +def test_stale_harness_observation_executes_fresh_once_at_public_archive(tmp_path, monkeypatch): + root, control, counter, accepted, task, command, first = _fixture(tmp_path, monkeypatch) + store_path = control / ".work-bundle/runtime/completion-provenance/completion-provenance-v1.json" + state = json.loads(store_path.read_text()) + state["observations"][0]["freshness_deadline"] = "2000-01-01T00:00:00Z" + store_path.write_text(json.dumps(state)) + + _public_archive(tmp_path, monkeypatch, control, root, command, accepted, task) + + assert counter.read_text() == "2" + repaired = json.loads(store_path.read_text()) + assert len(repaired["observations"]) == 2 + assert repaired["observations"][-1]["observation_id"] != first["observation_id"] def test_distinct_accepted_obligations_each_reuse_without_lifecycle_replay(tmp_path, monkeypatch): @@ -234,7 +273,7 @@ def test_executor_authored_receipt_id_is_not_independent_archive_proof(tmp_path, accepted["validation_evidence_ids"] = ["executor-forged-observation"] with pytest.raises(SystemExit, match="accepted harness observation"): - _archive(control, root, command, accepted, task) + _public_archive(tmp_path, monkeypatch, control, root, command, accepted, task) assert counter.read_text() == "1" From db8cee5501ea2978f84ffa6959e60db9a0a8cb1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 11:58:10 +0800 Subject: [PATCH 29/48] fix(orchestration): refresh malformed observations --- .../orchestration/completion_provenance.py | 8 +++++++- tests/test_completion_provenance.py | 19 +++++++++++++++++-- tests/test_orchestration_observation_reuse.py | 19 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/scripts/orchestration/completion_provenance.py b/scripts/orchestration/completion_provenance.py index 93fe37a..c2cdb13 100644 --- a/scripts/orchestration/completion_provenance.py +++ b/scripts/orchestration/completion_provenance.py @@ -544,7 +544,13 @@ def reuse_observation( if all(raw[field] == request[field] for field in OBSERVATION_IDENTITY_FIELDS) and ( _utc(raw["freshness_deadline"], "freshness_deadline") >= observed_at ): - _validate_result(raw["result"]) + try: + _validate_result(raw["result"]) + except (KeyError, TypeError, CompletionProvenanceError): + # A store-owned record can establish harness lineage + # without remaining capable positive evidence. Do not + # repair or replay it; obtain one fresh observation. + break return ObservationIdentityV1(**{**raw, "invocation_id": request["invocation_id"], "reuse_of": raw["observation_id"], "consumed_by_finalization": state["consumptions"].get(raw["observation_id"]), diff --git a/tests/test_completion_provenance.py b/tests/test_completion_provenance.py index 7206591..bce8614 100644 --- a/tests/test_completion_provenance.py +++ b/tests/test_completion_provenance.py @@ -87,8 +87,23 @@ def test_reuse_revalidates_stored_result_shape(tmp_path): state = store._read_unlocked() del state["observations"][0]["result"]["exit_code"] store._write_unlocked(state) - with pytest.raises(CompletionProvenanceError, match="closed and complete"): - reuse_observation(store, _request(observation_id="obs-002"), lambda: pytest.fail("must not execute"), now=NOW) + calls = 0 + + def execute(): + nonlocal calls + calls += 1 + return _result() + + replacement = reuse_observation( + store, + _request(observation_id="obs-002", invocation_id="invoke-002"), + execute, + now=NOW, + ) + + assert calls == 1 + assert replacement.observation_id == "obs-002" + assert replacement.reuse_of is None def _result(): diff --git a/tests/test_orchestration_observation_reuse.py b/tests/test_orchestration_observation_reuse.py index 55fcdc9..0d21059 100644 --- a/tests/test_orchestration_observation_reuse.py +++ b/tests/test_orchestration_observation_reuse.py @@ -221,6 +221,25 @@ def test_stale_harness_observation_executes_fresh_once_at_public_archive(tmp_pat assert repaired["observations"][-1]["observation_id"] != first["observation_id"] +def test_malformed_harness_observation_executes_fresh_once_at_public_archive(tmp_path, monkeypatch): + root, control, counter, accepted, task, command, first = _fixture(tmp_path, monkeypatch) + store_path = control / ".work-bundle/runtime/completion-provenance/completion-provenance-v1.json" + state = json.loads(store_path.read_text()) + del state["observations"][0]["result"]["exit_code"] + store_path.write_text(json.dumps(state)) + + _public_archive(tmp_path, monkeypatch, control, root, command, accepted, task) + + assert counter.read_text() == "2" + repaired = json.loads(store_path.read_text()) + assert len(repaired["observations"]) == 2 + replacement = repaired["observations"][-1] + assert replacement["observation_id"] != first["observation_id"] + assert repaired["consumptions"][replacement["observation_id"]] == ( + "archive:plan-001:task-001:VAL-001" + ) + + def test_distinct_accepted_obligations_each_reuse_without_lifecycle_replay(tmp_path, monkeypatch): root, control, counter, accepted, task, command, _ = _fixture(tmp_path, monkeypatch) other = deepcopy(task) From 7689cab3a417ebf5cfa2dce5e1047ba756124e96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 12:13:19 +0800 Subject: [PATCH 30/48] fix(orchestration): bind evidence to accepted authority --- scripts/orchestration/evaluation_identity.py | 55 +++++++ scripts/orchestration/execution_context.py | 2 + scripts/orchestration/review_runtime.py | 102 ++++++++++++ ...t_orchestration_evidence_classification.py | 147 ++++++++++++++++++ 4 files changed, 306 insertions(+) create mode 100644 tests/test_orchestration_evidence_classification.py diff --git a/scripts/orchestration/evaluation_identity.py b/scripts/orchestration/evaluation_identity.py index 80c8329..d075e16 100644 --- a/scripts/orchestration/evaluation_identity.py +++ b/scripts/orchestration/evaluation_identity.py @@ -293,6 +293,61 @@ def _product_identity(root: Path) -> Mapping[str, Any]: ) +def validation_interval_identity( + root: Path, + *, + baseline_revision: str, + endpoint_revision: str, + endpoint_mode: str, + manifest_path: Path, +) -> dict[str, Any]: + """Bind validation to an immutable historical interval or an explicit current HEAD. + + `endpoint_mode="frozen"` requires an exact commit id, so later repository growth + cannot expand historical authority. `endpoint_mode="current"` is the explicit + current-state contract and therefore requires the symbolic `HEAD` endpoint. + """ + + repository = root.expanduser().resolve() + if endpoint_mode not in {"frozen", "current"}: + raise EvaluationIdentityError("endpoint_mode must be frozen or current") + baseline = _git_oid(baseline_revision, "baseline_revision") + if endpoint_mode == "frozen": + if endpoint_revision == "HEAD": + raise EvaluationIdentityError("live HEAD requires an explicit current validation contract") + endpoint_requested = _git_oid(endpoint_revision, "endpoint_revision") + else: + if endpoint_revision != "HEAD": + raise EvaluationIdentityError("current validation contract must explicitly target HEAD") + endpoint_requested = "HEAD" + + baseline_commit = _git(repository, "rev-parse", f"{baseline}^{{commit}}") + endpoint_commit = _git(repository, "rev-parse", f"{endpoint_requested}^{{commit}}") + ancestry = subprocess.run( + ["git", "-C", str(repository), "merge-base", "--is-ancestor", baseline_commit, endpoint_commit], + capture_output=True, + text=True, + check=False, + ) + if ancestry.returncode != 0: + raise EvaluationIdentityError("validation baseline must be an ancestor of its endpoint") + manifest = manifest_path.expanduser().resolve() + return { + "schema": "validation-interval-identity-v1", + "repository": str(repository), + "endpoint_mode": endpoint_mode, + "baseline": { + "revision": baseline_commit, + "tree": _git(repository, "rev-parse", f"{baseline_commit}^{{tree}}"), + }, + "endpoint": { + "revision": endpoint_commit, + "tree": _git(repository, "rev-parse", f"{endpoint_commit}^{{tree}}"), + }, + "manifest": {"path": str(manifest), "sha256": _file_digest(manifest, "validation manifest")}, + } + + # Generated evidence is packaging, not product input. Other output locations # must be declared explicitly; never guess from a filename such as "result.json". OBSERVATION_ARTIFACT_ROOTS = ( diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index 50bf43c..870cc62 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -922,6 +922,8 @@ def project_validation_evidence( "digest": semantic_digest({"command": item.get("command"), "result": result}), "result": result, "expansion_reason": reason, + "authority_effect": "observation_only", + "lifecycle_action_authorized": False, "details": dict(item), }) continue diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index 4919629..3054191 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -28,6 +28,29 @@ MATERIAL_CHANGE_CLASSES = frozenset( {"material_redesign", "authority", "scope", "acceptance", "decomposition", "validation_allocation"} ) +EVIDENCE_CAUSAL_CLASSES = frozenset( + { + "claim_relevant_drift", + "implementation_defect", + "authority_plan_gap", + "evaluator_control_defect", + "non_claim_relevant", + } +) +EVIDENCE_CAUSAL_ROUTES: dict[str, tuple[str, str]] = { + "claim_relevant_drift": ("revalidate_claim", "route_current_owner"), + "implementation_defect": ("repair_task", "route_current_owner"), + "authority_plan_gap": ("repair_authority_plan", "route_current_owner"), + "evaluator_control_defect": ("repair_evaluator_control", "route_evaluator_control_owner"), + "non_claim_relevant": ("none", "diagnostic_only"), +} +EVIDENCE_CAUSAL_COMPARISONS = { + "claim_relevant_drift": "claim_relevant", + "implementation_defect": "claim_relevant", + "authority_plan_gap": "claim_relevant", + "evaluator_control_defect": "evaluator_only", + "non_claim_relevant": "unrelated", +} PARTICIPATION_FIELDS = ( "authorship", "repair_participation", @@ -82,6 +105,18 @@ } ) EVIDENCE_ITEM_KEYS = frozenset({"kind", "locator", "digest_or_identity", "observation"}) +EVIDENCE_CAUSAL_CLASSIFICATION_KEYS = frozenset( + { + "observation_reference", + "accepted_authority_comparison", + "causal_class", + "affected_claim", + "affected_owner", + "authorized_lifecycle_action", + "disposition", + } +) +ACCEPTED_AUTHORITY_COMPARISON_KEYS = frozenset({"authority_identity", "result", "basis"}) STAGE_REVIEW_KEYS = frozenset( {"review_id", "review_mode", "review_target_kind", "repair_frontier", "review_reset", "stage", "target_identity", "reviewer", "evidence", "verdict", "findings", "started_at", "completed_at", "staleness"} ) @@ -663,6 +698,19 @@ class ReviewFindingV1: disposition: str +@dataclass(frozen=True) +class EvidenceCausalClassificationV1: + """Agent-authored causal judgment required before evidence can route action.""" + + observation_reference: str + accepted_authority_comparison: Mapping[str, str] + causal_class: str + affected_claim: str + affected_owner: str + authorized_lifecycle_action: str + disposition: str + + @dataclass(frozen=True) class StageReviewV1: review_id: str @@ -789,6 +837,60 @@ def classify_first_broken_owner(finding_class: str) -> tuple[str, str, str]: raise ReviewContractError(f"class is not classified: {finding_class}") from error +def validate_evidence_causal_classification( + value: Mapping[str, Any], +) -> EvidenceCausalClassificationV1: + """Validate an agent's classification; this helper never infers a semantic class.""" + + record = _mapping(value, "evidence causal classification") + _closed(record, EVIDENCE_CAUSAL_CLASSIFICATION_KEYS, "evidence causal classification") + observation = _nonempty(record["observation_reference"], "observation_reference") + comparison = _mapping(record["accepted_authority_comparison"], "accepted_authority_comparison") + _closed(comparison, ACCEPTED_AUTHORITY_COMPARISON_KEYS, "accepted_authority_comparison") + authority_identity = _nonempty( + comparison["authority_identity"], "accepted_authority_comparison.authority_identity" + ) + basis = _nonempty(comparison["basis"], "accepted_authority_comparison.basis") + causal_class = _enum(record["causal_class"], EVIDENCE_CAUSAL_CLASSES, "causal_class") + expected_comparison = EVIDENCE_CAUSAL_COMPARISONS[causal_class] + if comparison["result"] != expected_comparison: + raise ReviewContractError("accepted authority comparison does not support the causal class") + affected_claim = _nonempty(record["affected_claim"], "affected_claim") + affected_owner = _nonempty(record["affected_owner"], "affected_owner") + expected_action, expected_disposition = EVIDENCE_CAUSAL_ROUTES[causal_class] + if record["authorized_lifecycle_action"] != expected_action: + raise ReviewContractError("authorized lifecycle action does not match the causal class") + if record["disposition"] != expected_disposition: + raise ReviewContractError("classification disposition does not match the causal class") + return EvidenceCausalClassificationV1( + observation_reference=observation, + accepted_authority_comparison={ + "authority_identity": authority_identity, + "result": expected_comparison, + "basis": basis, + }, + causal_class=causal_class, + affected_claim=affected_claim, + affected_owner=affected_owner, + authorized_lifecycle_action=expected_action, + disposition=expected_disposition, + ) + + +def review_remains_current_after_observation( + classification: Mapping[str, Any], *, reviewed_claim: str +) -> bool: + """Apply a classification to one review claim without treating raw evidence as authority.""" + + record = validate_evidence_causal_classification(classification) + claim = _nonempty(reviewed_claim, "reviewed_claim") + return not ( + record.causal_class + in {"claim_relevant_drift", "implementation_defect", "authority_plan_gap"} + and record.affected_claim == claim + ) + + def _affected_region(value: Any) -> dict[str, list[str]]: region = _mapping(value, "affected region") _closed(region, AFFECTED_REGION_KEYS, "affected region") diff --git a/tests/test_orchestration_evidence_classification.py b/tests/test_orchestration_evidence_classification.py new file mode 100644 index 0000000..7b312a6 --- /dev/null +++ b/tests/test_orchestration_evidence_classification.py @@ -0,0 +1,147 @@ +from __future__ import annotations + +import subprocess +import sys +from pathlib import Path + +import pytest + + +REPO_ROOT = Path(__file__).resolve().parents[1] +ORCHESTRATION = REPO_ROOT / "scripts" / "orchestration" +sys.path.insert(0, str(ORCHESTRATION)) + +from evaluation_identity import ( # noqa: E402 + EvaluationIdentityError, + validation_interval_identity, +) +from execution_context import project_validation_evidence # noqa: E402 +from review_runtime import ( # noqa: E402 + ReviewContractError, + review_remains_current_after_observation, + validate_evidence_causal_classification, +) + + +CAUSAL_ROUTES = { + "claim_relevant_drift": ("claim-owner", "revalidate_claim", "route_current_owner", "claim_relevant"), + "implementation_defect": ("task-owner", "repair_task", "route_current_owner", "claim_relevant"), + "authority_plan_gap": ("plan-owner", "repair_authority_plan", "route_current_owner", "claim_relevant"), + "evaluator_control_defect": ("evaluator-owner", "repair_evaluator_control", "route_evaluator_control_owner", "evaluator_only"), + "non_claim_relevant": ("controller", "none", "diagnostic_only", "unrelated"), +} + + +def classification(causal_class: str, *, claim: str = "claim-001") -> dict[str, object]: + owner, action, disposition, comparison = CAUSAL_ROUTES[causal_class] + return { + "observation_reference": "observation-001", + "accepted_authority_comparison": { + "authority_identity": "authority-sha256:abc", + "result": comparison, + "basis": "Compared the observation with the accepted claim and validation allocation.", + }, + "causal_class": causal_class, + "affected_claim": claim, + "affected_owner": owner, + "authorized_lifecycle_action": action, + "disposition": disposition, + } + + +@pytest.mark.parametrize("causal_class", list(CAUSAL_ROUTES)) +def test_agent_owned_causal_classification_routes_all_five_classes(causal_class: str) -> None: + record = classification(causal_class) + validated = validate_evidence_causal_classification(record) + assert validated.causal_class == causal_class + assert validated.authorized_lifecycle_action == record["authorized_lifecycle_action"] + + +def test_raw_or_misrouted_evidence_cannot_manufacture_lifecycle_authority() -> None: + with pytest.raises(ReviewContractError, match="classification"): + validate_evidence_causal_classification({"observation_reference": "failed-test"}) + + wrong = classification("non_claim_relevant") + wrong["authorized_lifecycle_action"] = "repair_task" + with pytest.raises(ReviewContractError, match="action"): + validate_evidence_causal_classification(wrong) + + projected = project_validation_evidence( + [{"id": "VAL-001", "command": "false", "result": "failed"}], + evidence_capability={"invariants": []}, + ) + assert projected[0]["authority_effect"] == "observation_only" + assert projected[0]["lifecycle_action_authorized"] is False + + +@pytest.mark.parametrize("causal_class", ["evaluator_control_defect", "non_claim_relevant"]) +def test_unrelated_or_evaluator_observation_keeps_independent_review_current(causal_class: str) -> None: + assert review_remains_current_after_observation( + classification(causal_class), reviewed_claim="claim-001" + ) + + +def test_only_claim_relevant_current_owner_class_can_reopen_matching_review_claim() -> None: + assert not review_remains_current_after_observation( + classification("implementation_defect"), reviewed_claim="claim-001" + ) + assert review_remains_current_after_observation( + classification("implementation_defect", claim="different-claim"), + reviewed_claim="claim-001", + ) + + +def git(root: Path, *args: str) -> str: + return subprocess.run( + ["git", "-C", str(root), *args], text=True, capture_output=True, check=True + ).stdout.strip() + + +def commit(root: Path, name: str, content: str) -> str: + (root / name).write_text(content, encoding="utf-8") + git(root, "add", name) + git(root, "commit", "-qm", content) + return git(root, "rev-parse", "HEAD") + + +def test_historical_validation_stays_bound_to_frozen_endpoint_after_later_commits(tmp_path: Path) -> None: + git(tmp_path, "init", "-q") + git(tmp_path, "config", "user.name", "Test") + git(tmp_path, "config", "user.email", "test@example.com") + baseline = commit(tmp_path, "product.txt", "baseline") + endpoint = commit(tmp_path, "product.txt", "accepted endpoint") + manifest = tmp_path / "manifest.json" + manifest.write_text('{"scope":"accepted"}', encoding="utf-8") + + before = validation_interval_identity( + tmp_path, baseline_revision=baseline, endpoint_revision=endpoint, + endpoint_mode="frozen", manifest_path=manifest, + ) + commit(tmp_path, "later.txt", "future work") + after = validation_interval_identity( + tmp_path, baseline_revision=baseline, endpoint_revision=endpoint, + endpoint_mode="frozen", manifest_path=manifest, + ) + assert after == before + assert after["endpoint"]["revision"] == endpoint + + +def test_live_head_requires_explicit_current_contract(tmp_path: Path) -> None: + git(tmp_path, "init", "-q") + git(tmp_path, "config", "user.name", "Test") + git(tmp_path, "config", "user.email", "test@example.com") + baseline = commit(tmp_path, "product.txt", "baseline") + manifest = tmp_path / "manifest.json" + manifest.write_text("{}", encoding="utf-8") + + with pytest.raises(EvaluationIdentityError, match="live HEAD"): + validation_interval_identity( + tmp_path, baseline_revision=baseline, endpoint_revision="HEAD", + endpoint_mode="frozen", manifest_path=manifest, + ) + + current = validation_interval_identity( + tmp_path, baseline_revision=baseline, endpoint_revision="HEAD", + endpoint_mode="current", manifest_path=manifest, + ) + assert current["endpoint"]["revision"] == git(tmp_path, "rev-parse", "HEAD") From 3d7e0c53bfba7003334b8308be1c94c15684a03e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 14:57:22 +0800 Subject: [PATCH 31/48] fix(orchestration): separate execution evidence from project CI --- .github/workflows/ci.yml | 3 - .../wor105/adversarial-result-v1.schema.json | 31 - .../components/contracts-v1.schema.json | 1838 ----------------- .../components/native-transition-record.yaml | 27 - .../wor105/components/task-f01r12-brief.yaml | 106 - .../wor105/components/task-f01r15-brief.yaml | 106 - .../wor105/components/task-f01r18-brief.yaml | 106 - evals/wor105/components/task-f01r2-brief.yaml | 100 - .../wor105/components/task-f01r25-brief.yaml | 141 -- evals/wor105/components/task-f01r9-brief.yaml | 106 - evals/wor105/fixtures/ADV-01.json | 30 - evals/wor105/fixtures/ADV-02.json | 28 - evals/wor105/fixtures/ADV-03.json | 18 - evals/wor105/fixtures/ADV-04.json | 16 - evals/wor105/fixtures/ADV-05.json | 19 - evals/wor105/fixtures/ADV-06.json | 18 - evals/wor105/fixtures/ADV-07.json | 16 - evals/wor105/fixtures/ADV-08.json | 21 - evals/wor105/fixtures/ADV-09.json | 18 - evals/wor105/fixtures/ADV-10.json | 18 - evals/wor105/fixtures/ADV-11.json | 17 - evals/wor105/fixtures/ADV-12.json | 18 - evals/wor105/freeze-manifest.json | 504 ----- evals/wor105/results.jsonl | 12 - evals/wor105/run.py | 217 -- evals/wor105/verify.py | 203 -- evals/wor108/contracts-v1.schema.json | 38 - evals/wor108/fixtures.json | 198 -- evals/wor108/migration-impact.json | 92 - evals/wor108/verify.py | 171 -- scripts/orchestration/core.py | 32 + scripts/orchestration/execution_context.py | 18 +- tests/test_ci_release_gate.py | 29 +- tests/test_execution_artifact_placement.py | 71 + ...orchestration_accepted_result_lifecycle.py | 17 - ... test_orchestration_context_projection.py} | 207 -- ... => test_orchestration_review_frontier.py} | 0 ... test_orchestration_subagent_ownership.py} | 0 tests/test_wor105_evals.py | 161 -- tests/test_wor105_native_dogfood.py | 173 -- tests/test_wor105_native_transition.py | 136 -- tests/test_wor108_closure.py | 108 - 42 files changed, 132 insertions(+), 5056 deletions(-) delete mode 100644 evals/wor105/adversarial-result-v1.schema.json delete mode 100644 evals/wor105/components/contracts-v1.schema.json delete mode 100644 evals/wor105/components/native-transition-record.yaml delete mode 100644 evals/wor105/components/task-f01r12-brief.yaml delete mode 100644 evals/wor105/components/task-f01r15-brief.yaml delete mode 100644 evals/wor105/components/task-f01r18-brief.yaml delete mode 100644 evals/wor105/components/task-f01r2-brief.yaml delete mode 100644 evals/wor105/components/task-f01r25-brief.yaml delete mode 100644 evals/wor105/components/task-f01r9-brief.yaml delete mode 100644 evals/wor105/fixtures/ADV-01.json delete mode 100644 evals/wor105/fixtures/ADV-02.json delete mode 100644 evals/wor105/fixtures/ADV-03.json delete mode 100644 evals/wor105/fixtures/ADV-04.json delete mode 100644 evals/wor105/fixtures/ADV-05.json delete mode 100644 evals/wor105/fixtures/ADV-06.json delete mode 100644 evals/wor105/fixtures/ADV-07.json delete mode 100644 evals/wor105/fixtures/ADV-08.json delete mode 100644 evals/wor105/fixtures/ADV-09.json delete mode 100644 evals/wor105/fixtures/ADV-10.json delete mode 100644 evals/wor105/fixtures/ADV-11.json delete mode 100644 evals/wor105/fixtures/ADV-12.json delete mode 100644 evals/wor105/freeze-manifest.json delete mode 100644 evals/wor105/results.jsonl delete mode 100644 evals/wor105/run.py delete mode 100644 evals/wor105/verify.py delete mode 100644 evals/wor108/contracts-v1.schema.json delete mode 100644 evals/wor108/fixtures.json delete mode 100644 evals/wor108/migration-impact.json delete mode 100644 evals/wor108/verify.py create mode 100644 tests/test_execution_artifact_placement.py rename tests/{test_wor108_context_projection.py => test_orchestration_context_projection.py} (87%) rename tests/{test_wor108_review_frontier.py => test_orchestration_review_frontier.py} (100%) rename tests/{test_wor108_subagent_ownership.py => test_orchestration_subagent_ownership.py} (100%) delete mode 100644 tests/test_wor105_evals.py delete mode 100644 tests/test_wor105_native_dogfood.py delete mode 100644 tests/test_wor105_native_transition.py delete mode 100644 tests/test_wor108_closure.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 78e8d0e..404e0df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,9 +24,6 @@ jobs: steps: - name: Checkout uses: actions/checkout@v5 - with: - # The frozen native-transition oracle verifies a historical kernel tree. - fetch-depth: 0 - name: Set up uv uses: astral-sh/setup-uv@v9.0.0 diff --git a/evals/wor105/adversarial-result-v1.schema.json b/evals/wor105/adversarial-result-v1.schema.json deleted file mode 100644 index fd52f80..0000000 --- a/evals/wor105/adversarial-result-v1.schema.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema":"https://json-schema.org/draft/2020-12/schema","$id":"urn:work-bundle:wor105:adversarial-result:v1","x-enforcement-mode":"bootstrap_policy","x-bootstrap-profile-sha256":"8d38967b95406362fe0db327309f53fca1b1b1906dbdfbdad8c30c13e03d87ad", - "$defs":{"sha":{"type":"string","pattern":"^[0-9a-f]{64}$"},"oid":{"type":"string","pattern":"^[0-9a-f]{40}$"},"id":{"type":"string","minLength":1},"ids":{"type":"array","items":{"$ref":"#/$defs/id"}},"text":{"type":"string","minLength":1}}, - "type":"object","additionalProperties":false,"required":["fixture_id","fixture_sha256","expected_decision","actual_decision","product_tree","specification_sha256","plan_sha256","task_identity","evaluation_id","component_digests","raw_evidence_sha256","adjudication_sha256","event_ids","proof","passed"], - "properties":{"fixture_id":{"type":"string","pattern":"^ADV-(0[1-9]|1[0-2])$"},"fixture_sha256":{"$ref":"#/$defs/sha"},"expected_decision":{"$ref":"#/$defs/text"},"actual_decision":{"$ref":"#/$defs/text"},"product_tree":{"$ref":"#/$defs/oid"},"specification_sha256":{"$ref":"#/$defs/sha"},"plan_sha256":{"$ref":"#/$defs/sha"},"task_identity":{"$ref":"#/$defs/id"},"evaluation_id":{"$ref":"#/$defs/id"},"component_digests":{"type":"object","additionalProperties":false,"required":["profile","fixtures","runner","verifier","result_schema","semantic_schema","instructions","evidence_capabilities"],"properties":{"profile":{"$ref":"#/$defs/sha"},"fixtures":{"$ref":"#/$defs/sha"},"runner":{"$ref":"#/$defs/sha"},"verifier":{"$ref":"#/$defs/sha"},"result_schema":{"$ref":"#/$defs/sha"},"semantic_schema":{"$ref":"#/$defs/sha"},"instructions":{"$ref":"#/$defs/sha"},"evidence_capabilities":{"$ref":"#/$defs/sha"}}},"raw_evidence_sha256":{"$ref":"#/$defs/sha"},"adjudication_sha256":{"$ref":"#/$defs/sha"},"event_ids":{"$ref":"#/$defs/ids"},"passed":{"const":true}, - "proof":{"type":"object","additionalProperties":false,"properties":{ - "source_sentinel_before_sha256":{"$ref":"#/$defs/sha"},"source_sentinel_after_sha256":{"$ref":"#/$defs/sha"},"control_sentinel_before_sha256":{"$ref":"#/$defs/sha"},"control_sentinel_after_sha256":{"$ref":"#/$defs/sha"},"denial_classes":{"type":"array","minItems":5,"maxItems":5,"items":{"const":"permission_denied"}},"allowed_read_output_sha256":{"$ref":"#/$defs/sha"},"validator_output_sha256":{"$ref":"#/$defs/sha"},"event_ids":{"type":"array","minItems":2,"maxItems":2,"uniqueItems":true,"items":{"$ref":"#/$defs/id"}}, - "original_evidence_sha256":{"$ref":"#/$defs/sha"},"expansion_event_ids":{"type":"array","minItems":2,"maxItems":2,"items":{"$ref":"#/$defs/id"}},"binding_state":{"const":"repair_owned"},"reslice_artifact_sha256":{"$ref":"#/$defs/sha"},"return_owner":{"const":"plan_owner"}, - "rejected_record_sha256":{"$ref":"#/$defs/sha"},"canonical_finding_sha256":{"$ref":"#/$defs/sha"},"validation_error_code":{"enum":["finding_route_mismatch","blocking_basis_required","placeholder_remote_forbidden"]},"advisory_id":{"$ref":"#/$defs/id"},"stage_state_before_sha256":{"$ref":"#/$defs/sha"},"stage_state_after_sha256":{"$ref":"#/$defs/sha"}, - "old_digest":{"$ref":"#/$defs/sha"},"new_digest":{"$ref":"#/$defs/sha"},"stale_run_id":{"$ref":"#/$defs/id"},"invalidation_id":{"$ref":"#/$defs/id"},"raw_response_before_sha256":{"$ref":"#/$defs/sha"},"raw_response_after_sha256":{"$ref":"#/$defs/sha"},"raw_trace_before_sha256":{"$ref":"#/$defs/sha"},"raw_trace_after_sha256":{"$ref":"#/$defs/sha"}, - "product_tree_before":{"$ref":"#/$defs/oid"},"product_tree_after":{"$ref":"#/$defs/oid"},"observation_before_sha256":{"$ref":"#/$defs/sha"},"observation_after_sha256":{"$ref":"#/$defs/sha"},"packaging_before":{"$ref":"#/$defs/oid"},"packaging_after":{"$ref":"#/$defs/oid"},"valid":{"const":true}, - "review_id":{"$ref":"#/$defs/id"},"target_before_sha256":{"$ref":"#/$defs/sha"},"target_after_sha256":{"$ref":"#/$defs/sha"},"staleness_reason":{"const":"target_identity_changed"},"countable_stage_reviews":{"const":0},"request_ids":{"type":"array","minItems":2,"maxItems":2,"uniqueItems":true,"items":{"$ref":"#/$defs/id"}},"subprocess_invocation_count":{"const":1},"observation_id":{"$ref":"#/$defs/id"},"reuse_of":{"$ref":"#/$defs/id"}, - "before_snapshot_sha256":{"$ref":"#/$defs/sha"},"after_snapshot_sha256":{"$ref":"#/$defs/sha"},"denial_event_ids":{"type":"array","minItems":2,"items":{"$ref":"#/$defs/id"}},"original_owner":{"$ref":"#/$defs/id"},"original_reason":{"$ref":"#/$defs/text"},"validation_error_codes":{"type":"array","minItems":4,"maxItems":4,"items":{"const":"reviewer_not_independent"}},"rejected_review_ids":{"type":"array","minItems":4,"maxItems":4,"uniqueItems":true,"items":{"$ref":"#/$defs/id"}}, - "public_contract_test_output_sha256":{"$ref":"#/$defs/sha"},"byte_oracle_failure_sha256":{"$ref":"#/$defs/sha"},"routed_finding_sha256":{"$ref":"#/$defs/sha"},"product_revision_before":{"$ref":"#/$defs/oid"},"product_revision_after":{"$ref":"#/$defs/oid"},"member_snapshot_sha256":{"$ref":"#/$defs/sha"},"checkout_absent":{"const":true},"origin_absent":{"const":true},"first_apply_state_sha256":{"$ref":"#/$defs/sha"},"replay_state_sha256":{"$ref":"#/$defs/sha"} - }} - }, - "allOf":[ - {"if":{"properties":{"fixture_id":{"const":"ADV-01"}}},"then":{"properties":{"expected_decision":{"const":"deny_mutation_and_protected_reads_allow_bounded_evidence"},"actual_decision":{"const":"deny_mutation_and_protected_reads_allow_bounded_evidence"},"proof":{"required":["source_sentinel_before_sha256","source_sentinel_after_sha256","control_sentinel_before_sha256","control_sentinel_after_sha256","denial_classes","allowed_read_output_sha256","validator_output_sha256","event_ids"],"propertyNames":{"enum":["source_sentinel_before_sha256","source_sentinel_after_sha256","control_sentinel_before_sha256","control_sentinel_after_sha256","denial_classes","allowed_read_output_sha256","validator_output_sha256","event_ids"]}}}}}, - {"if":{"properties":{"fixture_id":{"const":"ADV-02"}}},"then":{"properties":{"expected_decision":{"const":"pause_and_reslice_after_second_expansion"},"actual_decision":{"const":"pause_and_reslice_after_second_expansion"},"proof":{"required":["original_evidence_sha256","expansion_event_ids","binding_state","reslice_artifact_sha256","return_owner"],"propertyNames":{"enum":["original_evidence_sha256","expansion_event_ids","binding_state","reslice_artifact_sha256","return_owner"]}}}}}, - {"if":{"properties":{"fixture_id":{"const":"ADV-03"}}},"then":{"properties":{"expected_decision":{"const":"reject_and_route_allocation_gap_to_plan_reslice"},"actual_decision":{"const":"reject_and_route_allocation_gap_to_plan_reslice"},"proof":{"required":["rejected_record_sha256","canonical_finding_sha256","validation_error_code"],"properties":{"validation_error_code":{"const":"finding_route_mismatch"}},"propertyNames":{"enum":["rejected_record_sha256","canonical_finding_sha256","validation_error_code"]}}}}}, - {"if":{"properties":{"fixture_id":{"const":"ADV-04"}}},"then":{"properties":{"expected_decision":{"const":"reject_blocking_and_record_nonblocking_advisory"},"actual_decision":{"const":"reject_blocking_and_record_nonblocking_advisory"},"proof":{"required":["validation_error_code","advisory_id","stage_state_before_sha256","stage_state_after_sha256"],"properties":{"validation_error_code":{"const":"blocking_basis_required"}},"propertyNames":{"enum":["validation_error_code","advisory_id","stage_state_before_sha256","stage_state_after_sha256"]}}}}}, - {"if":{"properties":{"fixture_id":{"const":"ADV-05"}}},"then":{"properties":{"expected_decision":{"const":"stale_run_append_invalidation_preserve_raw_evidence"},"actual_decision":{"const":"stale_run_append_invalidation_preserve_raw_evidence"},"proof":{"required":["old_digest","new_digest","stale_run_id","invalidation_id","raw_response_before_sha256","raw_response_after_sha256","raw_trace_before_sha256","raw_trace_after_sha256"],"propertyNames":{"enum":["old_digest","new_digest","stale_run_id","invalidation_id","raw_response_before_sha256","raw_response_after_sha256","raw_trace_before_sha256","raw_trace_after_sha256"]}}}}}, - {"if":{"properties":{"fixture_id":{"const":"ADV-06"}}},"then":{"properties":{"expected_decision":{"const":"preserve_product_observation_update_packaging_only"},"actual_decision":{"const":"preserve_product_observation_update_packaging_only"},"proof":{"required":["product_tree_before","product_tree_after","observation_before_sha256","observation_after_sha256","packaging_before","packaging_after","valid"],"propertyNames":{"enum":["product_tree_before","product_tree_after","observation_before_sha256","observation_after_sha256","packaging_before","packaging_after","valid"]}}}}}, - {"if":{"properties":{"fixture_id":{"const":"ADV-07"}}},"then":{"properties":{"expected_decision":{"const":"mark_review_stale_and_remove_stage_credit"},"actual_decision":{"const":"mark_review_stale_and_remove_stage_credit"},"proof":{"required":["review_id","target_before_sha256","target_after_sha256","staleness_reason","countable_stage_reviews"],"propertyNames":{"enum":["review_id","target_before_sha256","target_after_sha256","staleness_reason","countable_stage_reviews"]}}}}}, - {"if":{"properties":{"fixture_id":{"const":"ADV-08"}}},"then":{"properties":{"expected_decision":{"const":"execute_once_and_reuse_observation"},"actual_decision":{"const":"execute_once_and_reuse_observation"},"proof":{"required":["request_ids","subprocess_invocation_count","observation_id","reuse_of"],"propertyNames":{"enum":["request_ids","subprocess_invocation_count","observation_id","reuse_of"]}}}}}, - {"if":{"properties":{"fixture_id":{"const":"ADV-09"}}},"then":{"properties":{"expected_decision":{"const":"deny_release_preserve_owner_reason_history"},"actual_decision":{"const":"deny_release_preserve_owner_reason_history"},"proof":{"required":["before_snapshot_sha256","after_snapshot_sha256","denial_event_ids","original_owner","original_reason"],"propertyNames":{"enum":["before_snapshot_sha256","after_snapshot_sha256","denial_event_ids","original_owner","original_reason"]}}}}}, - {"if":{"properties":{"fixture_id":{"const":"ADV-10"}}},"then":{"properties":{"expected_decision":{"const":"reject_each_and_require_fresh_reviewer"},"actual_decision":{"const":"reject_each_and_require_fresh_reviewer"},"proof":{"required":["validation_error_codes","rejected_review_ids","countable_stage_reviews"],"propertyNames":{"enum":["validation_error_codes","rejected_review_ids","countable_stage_reviews"]}}}}}, - {"if":{"properties":{"fixture_id":{"const":"ADV-11"}}},"then":{"properties":{"expected_decision":{"const":"route_validation_oracle_defect_without_product_rollback"},"actual_decision":{"const":"route_validation_oracle_defect_without_product_rollback"},"proof":{"required":["public_contract_test_output_sha256","byte_oracle_failure_sha256","routed_finding_sha256","product_revision_before","product_revision_after"],"propertyNames":{"enum":["public_contract_test_output_sha256","byte_oracle_failure_sha256","routed_finding_sha256","product_revision_before","product_revision_after"]}}}}}, - {"if":{"properties":{"fixture_id":{"const":"ADV-12"}}},"then":{"properties":{"expected_decision":{"const":"reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop"},"actual_decision":{"const":"reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop"},"proof":{"required":["validation_error_code","member_snapshot_sha256","checkout_absent","origin_absent","first_apply_state_sha256","replay_state_sha256"],"properties":{"validation_error_code":{"const":"placeholder_remote_forbidden"}},"propertyNames":{"enum":["validation_error_code","member_snapshot_sha256","checkout_absent","origin_absent","first_apply_state_sha256","replay_state_sha256"]}}}}} - ] -} diff --git a/evals/wor105/components/contracts-v1.schema.json b/evals/wor105/components/contracts-v1.schema.json deleted file mode 100644 index 6c59c00..0000000 --- a/evals/wor105/components/contracts-v1.schema.json +++ /dev/null @@ -1,1838 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "urn:work-bundle:wor105:contracts:v1", - "x-enforcement-mode": "bootstrap_policy", - "x-bootstrap-profile-sha256": "8d38967b95406362fe0db327309f53fca1b1b1906dbdfbdad8c30c13e03d87ad", - "$defs": { - "id": { - "type": "string", - "minLength": 1, - "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]*$" - }, - "sha256": { - "type": "string", - "pattern": "^[0-9a-f]{64}$" - }, - "gitOid": { - "type": "string", - "pattern": "^[0-9a-f]{40}$" - }, - "time": { - "type": "string", - "format": "date-time", - "pattern": "Z$" - }, - "nullableId": { - "anyOf": [ - { - "$ref": "#/$defs/id" - }, - { - "type": "null" - } - ] - }, - "nullableString": { - "type": [ - "string", - "null" - ] - }, - "evidenceItem": { - "type": "object", - "additionalProperties": false, - "required": [ - "kind", - "locator", - "digest_or_identity", - "observation" - ], - "properties": { - "kind": { - "enum": [ - "authority", - "source", - "test", - "runtime", - "environment" - ] - }, - "locator": { - "type": "string", - "minLength": 1 - }, - "digest_or_identity": { - "type": "string", - "minLength": 1 - }, - "observation": { - "type": "string", - "minLength": 1 - } - } - }, - "targetIdentity": { - "type": "object", - "additionalProperties": false, - "required": [ - "artifact_id", - "revision", - "sha256", - "source_tree" - ], - "properties": { - "artifact_id": { - "$ref": "#/$defs/id" - }, - "revision": { - "type": "string", - "minLength": 1 - }, - "sha256": { - "$ref": "#/$defs/sha256" - }, - "source_tree": { - "anyOf": [ - { - "$ref": "#/$defs/gitOid" - }, - { - "type": "null" - } - ] - } - } - }, - "reviewFinding": { - "type": "object", - "additionalProperties": false, - "required": [ - "finding_id", - "stage", - "class", - "severity", - "first_broken_artifact", - "obligation_basis", - "evidence", - "target_identity", - "summary", - "recommended_owner", - "disposition" - ], - "properties": { - "finding_id": { - "$ref": "#/$defs/id" - }, - "stage": { - "enum": [ - "specification", - "plan", - "implementation", - "validation", - "environment" - ] - }, - "class": { - "enum": [ - "specification_gap", - "decomposition_gap", - "allocation_gap", - "implementation_defect", - "validation_oracle_defect", - "environment_failure", - "advisory_enhancement" - ] - }, - "severity": { - "enum": [ - "blocking", - "non_blocking", - "advisory" - ] - }, - "first_broken_artifact": { - "enum": [ - "specification", - "plan", - "task", - "implementation", - "validation_oracle", - "environment" - ] - }, - "obligation_basis": { - "enum": [ - "accepted_requirement", - "essential_safety", - "evidence_integrity", - "none" - ] - }, - "evidence": { - "type": "array", - "items": { - "$ref": "#/$defs/evidenceItem" - } - }, - "target_identity": { - "$ref": "#/$defs/targetIdentity" - }, - "summary": { - "type": "string", - "minLength": 1 - }, - "recommended_owner": { - "enum": [ - "specification_owner", - "plan_owner", - "task_owner", - "oracle_owner", - "environment_owner", - "backlog_owner" - ] - }, - "disposition": { - "enum": [ - "reopen_specification", - "repair_plan", - "reslice_plan", - "repair_task", - "repair_oracle", - "recover_environment", - "record_advisory", - "accepted", - "rejected" - ] - } - } - }, - "stageReview": { - "type": "object", - "additionalProperties": false, - "required": [ - "review_id", - "stage", - "target_identity", - "reviewer", - "evidence", - "verdict", - "findings", - "started_at", - "completed_at", - "staleness" - ], - "properties": { - "review_id": { - "$ref": "#/$defs/id" - }, - "stage": { - "enum": [ - "specification", - "plan", - "integrated_implementation" - ] - }, - "target_identity": { - "$ref": "#/$defs/targetIdentity" - }, - "reviewer": { - "type": "object", - "additionalProperties": false, - "required": [ - "agent_id", - "capability", - "authorship", - "repair_participation", - "decision_participation", - "deliberation_participation", - "context_origin" - ], - "properties": { - "agent_id": { - "$ref": "#/$defs/id" - }, - "capability": { - "type": "string", - "minLength": 1 - }, - "authorship": { - "enum": [ - "none", - "present" - ] - }, - "repair_participation": { - "enum": [ - "none", - "present" - ] - }, - "decision_participation": { - "enum": [ - "none", - "present" - ] - }, - "deliberation_participation": { - "enum": [ - "none", - "present" - ] - }, - "context_origin": { - "enum": [ - "direct_source", - "carried_summary" - ] - } - } - }, - "evidence": { - "type": "object", - "additionalProperties": false, - "required": [ - "mode", - "capabilities", - "unavailable_evidence", - "commands", - "artifacts" - ], - "properties": { - "mode": { - "enum": [ - "direct", - "constrained_direct" - ] - }, - "capabilities": { - "type": "array", - "items": { - "type": "string", - "minLength": 1 - } - }, - "unavailable_evidence": { - "type": "array", - "items": { - "type": "string", - "minLength": 1 - } - }, - "commands": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "command_id", - "purpose", - "exit_code", - "output_digest" - ], - "properties": { - "command_id": { - "$ref": "#/$defs/id" - }, - "purpose": { - "type": "string", - "minLength": 1 - }, - "exit_code": { - "type": "integer" - }, - "output_digest": { - "$ref": "#/$defs/sha256" - } - } - } - }, - "artifacts": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "path", - "sha256" - ], - "properties": { - "path": { - "type": "string", - "minLength": 1 - }, - "sha256": { - "$ref": "#/$defs/sha256" - } - } - } - } - } - }, - "verdict": { - "enum": [ - "accepted", - "repair", - "blocked" - ] - }, - "findings": { - "type": "array", - "items": { - "$ref": "#/$defs/reviewFinding" - } - }, - "started_at": { - "$ref": "#/$defs/time" - }, - "completed_at": { - "$ref": "#/$defs/time" - }, - "staleness": { - "type": "object", - "additionalProperties": false, - "required": [ - "is_stale", - "reason", - "supersedes" - ], - "properties": { - "is_stale": { - "type": "boolean" - }, - "reason": { - "$ref": "#/$defs/nullableString" - }, - "supersedes": { - "$ref": "#/$defs/nullableId" - } - } - } - } - }, - "evaluationIdentity": { - "type": "object", - "additionalProperties": false, - "required": [ - "evaluation_id", - "product", - "specification", - "task_set_digest", - "instruction_digest", - "fixture_digest", - "runner_digest", - "verifier_digest", - "semantic_schema_digest", - "evidence_capabilities_digest", - "invocation_digest", - "raw_response_digest", - "raw_trace_digest", - "adjudication_digest", - "packaging", - "status", - "invalidations" - ], - "properties": { - "evaluation_id": { - "$ref": "#/$defs/id" - }, - "product": { - "$ref": "#/$defs/gitPoint" - }, - "specification": { - "$ref": "#/$defs/targetIdentity" - }, - "task_set_digest": { - "$ref": "#/$defs/sha256" - }, - "instruction_digest": { - "$ref": "#/$defs/sha256" - }, - "fixture_digest": { - "$ref": "#/$defs/sha256" - }, - "runner_digest": { - "$ref": "#/$defs/sha256" - }, - "verifier_digest": { - "$ref": "#/$defs/sha256" - }, - "semantic_schema_digest": { - "$ref": "#/$defs/sha256" - }, - "evidence_capabilities_digest": { - "$ref": "#/$defs/sha256" - }, - "invocation_digest": { - "$ref": "#/$defs/sha256" - }, - "raw_response_digest": { - "$ref": "#/$defs/sha256" - }, - "raw_trace_digest": { - "$ref": "#/$defs/sha256" - }, - "adjudication_digest": { - "$ref": "#/$defs/sha256" - }, - "packaging": { - "anyOf": [ - { - "$ref": "#/$defs/gitPoint" - }, - { - "type": "null" - } - ] - }, - "status": { - "enum": [ - "frozen", - "valid", - "stale", - "invalid" - ] - }, - "invalidations": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "invalidation_id", - "changed_component", - "old_digest", - "new_digest", - "affected_run_ids", - "reason", - "timestamp" - ], - "properties": { - "invalidation_id": { - "$ref": "#/$defs/id" - }, - "changed_component": { - "enum": [ - "product", - "specification", - "tasks", - "instruction", - "fixture", - "runner", - "verifier", - "semantic_schema", - "evidence_capabilities" - ] - }, - "old_digest": { - "$ref": "#/$defs/sha256" - }, - "new_digest": { - "$ref": "#/$defs/sha256" - }, - "affected_run_ids": { - "type": "array", - "minItems": 1, - "items": { - "$ref": "#/$defs/id" - } - }, - "reason": { - "type": "string", - "minLength": 1 - }, - "timestamp": { - "$ref": "#/$defs/time" - } - } - } - } - } - }, - "gitPoint": { - "type": "object", - "additionalProperties": false, - "required": [ - "repository", - "revision", - "tree" - ], - "properties": { - "repository": { - "type": "string", - "minLength": 1 - }, - "revision": { - "$ref": "#/$defs/gitOid" - }, - "tree": { - "$ref": "#/$defs/gitOid" - } - } - }, - "stageEvent": { - "type": "object", - "additionalProperties": false, - "required": [ - "event_id", - "timestamp", - "process_id", - "stage", - "attempt_id", - "event_type", - "enforcement_mode", - "join_ids", - "clocks", - "finding_class", - "return_reason", - "owner", - "identity", - "privacy" - ], - "properties": { - "event_id": { - "$ref": "#/$defs/id" - }, - "timestamp": { - "$ref": "#/$defs/time" - }, - "process_id": { - "$ref": "#/$defs/id" - }, - "stage": { - "type": "string", - "minLength": 1 - }, - "attempt_id": { - "$ref": "#/$defs/id" - }, - "event_type": { - "enum": [ - "stage_started", - "stage_completed", - "finding_recorded", - "work_returned", - "reslice_recorded", - "suite_started", - "suite_reused", - "suite_completed", - "evidence_invalidated", - "reviewer_mutation_denied", - "control_plane_repaired", - "binding_retained", - "binding_released" - ] - }, - "enforcement_mode": { - "enum": [ - "bootstrap_policy", - "native" - ] - }, - "join_ids": { - "type": "object", - "additionalProperties": false, - "required": [ - "specification_id", - "plan_id", - "phase_id", - "task_id", - "review_id", - "evaluation_id" - ], - "properties": { - "specification_id": { - "$ref": "#/$defs/nullableId" - }, - "plan_id": { - "$ref": "#/$defs/nullableId" - }, - "phase_id": { - "$ref": "#/$defs/nullableId" - }, - "task_id": { - "$ref": "#/$defs/nullableId" - }, - "review_id": { - "$ref": "#/$defs/nullableId" - }, - "evaluation_id": { - "$ref": "#/$defs/nullableId" - } - } - }, - "clocks": { - "type": "object", - "additionalProperties": false, - "required": [ - "wall_ms", - "active_ms", - "billed_ms" - ], - "properties": { - "wall_ms": { - "type": "integer", - "minimum": 0 - }, - "active_ms": { - "type": "integer", - "minimum": 0 - }, - "billed_ms": { - "type": [ - "integer", - "null" - ], - "minimum": 0 - } - } - }, - "finding_class": { - "type": [ - "string", - "null" - ] - }, - "return_reason": { - "$ref": "#/$defs/nullableString" - }, - "owner": { - "$ref": "#/$defs/nullableString" - }, - "identity": { - "type": "object", - "additionalProperties": false, - "required": [ - "product_tree", - "artifact_digest", - "mutation_epoch" - ], - "properties": { - "product_tree": { - "anyOf": [ - { - "$ref": "#/$defs/gitOid" - }, - { - "type": "null" - } - ] - }, - "artifact_digest": { - "anyOf": [ - { - "$ref": "#/$defs/sha256" - }, - { - "type": "null" - } - ] - }, - "mutation_epoch": { - "type": [ - "integer", - "null" - ], - "minimum": 0 - } - } - }, - "privacy": { - "const": "operational_metadata_only" - } - } - }, - "observationIdentity": { - "type": "object", - "additionalProperties": false, - "required": [ - "observation_id", - "command_digest", - "cwd_token", - "product_tree", - "state_digest", - "oracle_digest", - "freshness_deadline", - "mutation_epoch", - "invocation_id", - "result", - "reuse_of", - "consumed_by_finalization" - ], - "properties": { - "observation_id": { - "$ref": "#/$defs/id" - }, - "command_digest": { - "$ref": "#/$defs/sha256" - }, - "cwd_token": { - "enum": [ - "workspace_root", - "bound_project_root", - "isolated_execution_root" - ] - }, - "product_tree": { - "$ref": "#/$defs/gitOid" - }, - "state_digest": { - "$ref": "#/$defs/sha256" - }, - "oracle_digest": { - "$ref": "#/$defs/sha256" - }, - "freshness_deadline": { - "$ref": "#/$defs/time" - }, - "mutation_epoch": { - "type": "integer", - "minimum": 0 - }, - "invocation_id": { - "$ref": "#/$defs/id" - }, - "result": { - "type": "object", - "additionalProperties": false, - "required": [ - "exit_code", - "stdout_digest", - "stderr_digest", - "started_at", - "completed_at" - ], - "properties": { - "exit_code": { - "type": "integer" - }, - "stdout_digest": { - "$ref": "#/$defs/sha256" - }, - "stderr_digest": { - "$ref": "#/$defs/sha256" - }, - "started_at": { - "$ref": "#/$defs/time" - }, - "completed_at": { - "$ref": "#/$defs/time" - } - } - }, - "reuse_of": { - "$ref": "#/$defs/nullableId" - }, - "consumed_by_finalization": { - "$ref": "#/$defs/nullableId" - } - } - }, - "bindingOwnership": { - "type": "object", - "additionalProperties": false, - "required": [ - "binding_id", - "target_kind", - "state", - "original_owner", - "current_owner", - "reason", - "repair_owner", - "rereview_owner", - "releasable", - "history" - ], - "properties": { - "binding_id": { - "$ref": "#/$defs/id" - }, - "target_kind": { - "enum": [ - "local_project", - "git_backed", - "isolated_worktree" - ] - }, - "state": { - "enum": [ - "active", - "repair_owned", - "rereview_owned", - "releasable", - "released" - ] - }, - "original_owner": { - "$ref": "#/$defs/id" - }, - "current_owner": { - "$ref": "#/$defs/id" - }, - "reason": { - "type": "string", - "minLength": 1 - }, - "repair_owner": { - "$ref": "#/$defs/nullableId" - }, - "rereview_owner": { - "$ref": "#/$defs/nullableId" - }, - "releasable": { - "type": "boolean" - }, - "history": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "transition_id", - "from", - "to", - "owner", - "reason", - "timestamp" - ], - "properties": { - "transition_id": { - "$ref": "#/$defs/id" - }, - "from": { - "type": [ - "string", - "null" - ] - }, - "to": { - "enum": [ - "active", - "repair_owned", - "rereview_owned", - "releasable", - "released" - ] - }, - "owner": { - "$ref": "#/$defs/id" - }, - "reason": { - "type": "string", - "minLength": 1 - }, - "timestamp": { - "$ref": "#/$defs/time" - } - } - } - } - } - }, - "deferredMember": { - "type": "object", - "additionalProperties": false, - "required": [ - "member_id", - "repository_id", - "portable_path", - "default_branch", - "required", - "remote", - "materialization", - "proposal_id", - "transaction_id", - "replay_key", - "device_binding" - ], - "properties": { - "member_id": { - "$ref": "#/$defs/id" - }, - "repository_id": { - "$ref": "#/$defs/id" - }, - "portable_path": { - "type": "string", - "pattern": "^[^/]" - }, - "default_branch": { - "type": "string", - "minLength": 1 - }, - "required": { - "type": "boolean" - }, - "remote": { - "type": [ - "string", - "null" - ], - "format": "uri" - }, - "materialization": { - "enum": [ - "deferred", - "attaching", - "attached", - "failed" - ] - }, - "proposal_id": { - "$ref": "#/$defs/id" - }, - "transaction_id": { - "$ref": "#/$defs/id" - }, - "replay_key": { - "$ref": "#/$defs/sha256" - }, - "device_binding": { - "anyOf": [ - { - "type": "null" - }, - { - "type": "object", - "additionalProperties": false, - "required": [ - "device_id", - "checkout_path_token", - "remote_fingerprint", - "observed_revision", - "observed_tree" - ], - "properties": { - "device_id": { - "$ref": "#/$defs/id" - }, - "checkout_path_token": { - "type": "string", - "minLength": 1 - }, - "remote_fingerprint": { - "$ref": "#/$defs/sha256" - }, - "observed_revision": { - "$ref": "#/$defs/gitOid" - }, - "observed_tree": { - "$ref": "#/$defs/gitOid" - } - } - } - ] - } - } - }, - "capabilityIndex": { - "type": "object", - "additionalProperties": false, - "required": [ - "schema_version", - "index_id", - "nodes", - "relations", - "evidence", - "generated_at", - "source_digest" - ], - "properties": { - "schema_version": { - "const": "1" - }, - "index_id": { - "$ref": "#/$defs/id" - }, - "generated_at": { - "$ref": "#/$defs/time" - }, - "source_digest": { - "$ref": "#/$defs/sha256" - }, - "nodes": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "node_id", - "kind", - "title", - "summary", - "actor", - "outcome", - "preconditions", - "effects", - "failures", - "policies", - "aliases", - "lifecycle", - "freshness", - "parent_id", - "evidence_ids" - ], - "properties": { - "node_id": { - "$ref": "#/$defs/id" - }, - "kind": { - "enum": [ - "domain", - "capability", - "constraint_capability" - ] - }, - "title": { - "type": "string", - "minLength": 1 - }, - "summary": { - "type": "string", - "minLength": 1 - }, - "actor": { - "type": "string", - "minLength": 1 - }, - "outcome": { - "type": "string", - "minLength": 1 - }, - "preconditions": { - "type": "array", - "items": { - "type": "string" - } - }, - "effects": { - "type": "array", - "items": { - "type": "string" - } - }, - "failures": { - "type": "array", - "items": { - "type": "string" - } - }, - "policies": { - "type": "array", - "items": { - "type": "string" - } - }, - "aliases": { - "type": "array", - "items": { - "type": "string" - } - }, - "lifecycle": { - "enum": [ - "candidate", - "grounded", - "accepted", - "superseded" - ] - }, - "freshness": { - "enum": [ - "current", - "stale" - ] - }, - "parent_id": { - "$ref": "#/$defs/nullableId" - }, - "evidence_ids": { - "type": "array", - "items": { - "$ref": "#/$defs/id" - } - } - } - } - }, - "relations": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "relation_id", - "from_id", - "to_id", - "type", - "evidence_ids" - ], - "properties": { - "relation_id": { - "$ref": "#/$defs/id" - }, - "from_id": { - "$ref": "#/$defs/id" - }, - "to_id": { - "$ref": "#/$defs/id" - }, - "type": { - "enum": [ - "contains", - "requires", - "constrains", - "produces", - "consumes", - "transitions", - "validates", - "conflicts_with", - "related_to" - ] - }, - "evidence_ids": { - "type": "array", - "items": { - "$ref": "#/$defs/id" - } - } - } - } - }, - "evidence": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "evidence_id", - "kind", - "locator", - "identity", - "authority", - "observed_at" - ], - "properties": { - "evidence_id": { - "$ref": "#/$defs/id" - }, - "kind": { - "enum": [ - "accepted_spec", - "knowledge", - "public_contract", - "integration_test", - "history", - "source" - ] - }, - "locator": { - "type": "string", - "minLength": 1 - }, - "identity": { - "type": "string", - "minLength": 1 - }, - "authority": { - "type": "boolean" - }, - "observed_at": { - "$ref": "#/$defs/time" - } - } - } - } - } - }, - "neighborhood": { - "type": "object", - "additionalProperties": false, - "required": [ - "query_id", - "query_text_digest", - "depth", - "obligations", - "triggers", - "inclusions", - "exclusions", - "frontier", - "gaps", - "stopping_reason", - "source_index_digest" - ], - "properties": { - "query_id": { - "$ref": "#/$defs/id" - }, - "query_text_digest": { - "$ref": "#/$defs/sha256" - }, - "depth": { - "enum": [ - "light", - "standard", - "deep" - ] - }, - "obligations": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "obligation_id", - "kind", - "status", - "evidence_ids" - ], - "properties": { - "obligation_id": { - "$ref": "#/$defs/id" - }, - "kind": { - "type": "string", - "minLength": 1 - }, - "status": { - "enum": [ - "satisfied", - "not_applicable", - "blocked" - ] - }, - "evidence_ids": { - "type": "array", - "items": { - "$ref": "#/$defs/id" - } - } - } - } - }, - "triggers": { - "type": "array", - "uniqueItems": true, - "items": { - "enum": [ - "permission", - "ownership", - "destructive_effect", - "data_effect", - "external_effect", - "state_transition", - "compatibility", - "evidence_conflict" - ] - } - }, - "inclusions": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "node_id", - "reason", - "rank", - "evidence_ids" - ], - "properties": { - "node_id": { - "$ref": "#/$defs/id" - }, - "reason": { - "type": "string", - "minLength": 1 - }, - "rank": { - "type": "number" - }, - "evidence_ids": { - "type": "array", - "items": { - "$ref": "#/$defs/id" - } - } - } - } - }, - "exclusions": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "node_id", - "reason" - ], - "properties": { - "node_id": { - "$ref": "#/$defs/id" - }, - "reason": { - "type": "string", - "minLength": 1 - } - } - } - }, - "frontier": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "node_id", - "information_value", - "evidence_cost", - "miss_risk", - "expand" - ], - "properties": { - "node_id": { - "$ref": "#/$defs/id" - }, - "information_value": { - "type": "number", - "minimum": 0, - "maximum": 1 - }, - "evidence_cost": { - "type": "number", - "minimum": 0, - "maximum": 1 - }, - "miss_risk": { - "type": "number", - "minimum": 0, - "maximum": 1 - }, - "expand": { - "type": "boolean" - } - } - } - }, - "gaps": { - "type": "array", - "items": { - "type": "string", - "minLength": 1 - } - }, - "stopping_reason": { - "type": "string", - "minLength": 1 - }, - "source_index_digest": { - "$ref": "#/$defs/sha256" - } - } - }, - "migrationHandoff": { - "type": "object", - "additionalProperties": false, - "required": [ - "handoff_id", - "issue", - "source_release", - "accepted_reviews", - "evaluation", - "knowledge_closure", - "mcp_checkpoint", - "capability_contracts", - "deferred_remote_contract", - "telemetry_export", - "open_risks", - "excluded_work", - "resume_preconditions", - "created_at" - ], - "properties": { - "handoff_id": { - "$ref": "#/$defs/id" - }, - "issue": { - "const": "WOR-107" - }, - "source_release": { - "type": "object", - "additionalProperties": false, - "required": [ - "branch", - "commit", - "tree", - "version", - "tag" - ], - "properties": { - "branch": { - "type": "string", - "minLength": 1 - }, - "commit": { - "$ref": "#/$defs/gitOid" - }, - "tree": { - "$ref": "#/$defs/gitOid" - }, - "version": { - "type": "string", - "minLength": 1 - }, - "tag": { - "$ref": "#/$defs/nullableString" - } - } - }, - "accepted_reviews": { - "type": "array", - "minItems": 3, - "maxItems": 3, - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "stage", - "review_id", - "target_digest", - "source_tree" - ], - "properties": { - "stage": { - "enum": [ - "specification", - "plan", - "integrated_implementation" - ] - }, - "review_id": { - "$ref": "#/$defs/id" - }, - "target_digest": { - "$ref": "#/$defs/sha256" - }, - "source_tree": { - "anyOf": [ - { - "$ref": "#/$defs/gitOid" - }, - { - "type": "null" - } - ] - } - } - } - }, - "evaluation": { - "type": "object", - "additionalProperties": false, - "required": [ - "evaluation_id", - "profile_digest", - "fixture_digest", - "runner_digest", - "verifier_digest", - "schema_digest", - "result_digest" - ], - "properties": { - "evaluation_id": { - "$ref": "#/$defs/id" - }, - "profile_digest": { - "$ref": "#/$defs/sha256" - }, - "fixture_digest": { - "$ref": "#/$defs/sha256" - }, - "runner_digest": { - "$ref": "#/$defs/sha256" - }, - "verifier_digest": { - "$ref": "#/$defs/sha256" - }, - "schema_digest": { - "$ref": "#/$defs/sha256" - }, - "result_digest": { - "$ref": "#/$defs/sha256" - } - } - }, - "knowledge_closure": { - "type": "object", - "additionalProperties": false, - "required": [ - "disposition", - "note_ids", - "index_digest", - "validation_evidence" - ], - "properties": { - "disposition": { - "enum": [ - "none", - "update", - "supersede", - "reclassify" - ] - }, - "note_ids": { - "type": "array", - "items": { - "$ref": "#/$defs/id" - } - }, - "index_digest": { - "$ref": "#/$defs/sha256" - }, - "validation_evidence": { - "type": "array", - "minItems": 1, - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "command", - "exit_code", - "output_digest" - ], - "properties": { - "command": { - "type": "string", - "minLength": 1 - }, - "exit_code": { - "type": "integer" - }, - "output_digest": { - "$ref": "#/$defs/sha256" - } - } - } - } - } - }, - "mcp_checkpoint": { - "type": "object", - "additionalProperties": false, - "required": [ - "step01", - "step02", - "step03", - "task009_seal", - "duplicate_handoff_ambiguity", - "canonical_main" - ], - "properties": { - "step01": { - "$ref": "#/$defs/gitPoint" - }, - "step02": { - "$ref": "#/$defs/gitPoint" - }, - "step03": { - "$ref": "#/$defs/gitPoint" - }, - "canonical_main": { - "$ref": "#/$defs/gitPoint" - }, - "task009_seal": { - "type": "object", - "additionalProperties": false, - "required": [ - "ref", - "commit", - "tree", - "bundle_sha256", - "manifest_sha256" - ], - "properties": { - "ref": { - "type": "string", - "minLength": 1 - }, - "commit": { - "$ref": "#/$defs/gitOid" - }, - "tree": { - "$ref": "#/$defs/gitOid" - }, - "bundle_sha256": { - "$ref": "#/$defs/sha256" - }, - "manifest_sha256": { - "$ref": "#/$defs/sha256" - } - } - }, - "duplicate_handoff_ambiguity": { - "type": "object", - "additionalProperties": false, - "required": [ - "status", - "task", - "owner", - "summary" - ], - "properties": { - "status": { - "const": "unresolved" - }, - "task": { - "const": "Task008" - }, - "owner": { - "const": "WOR-107" - }, - "summary": { - "type": "string", - "minLength": 1 - } - } - } - } - }, - "capability_contracts": { - "type": "array", - "minItems": 9, - "maxItems": 9, - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "api_id", - "schema", - "version", - "digest" - ], - "properties": { - "api_id": { - "pattern": "^API-00[1-9]$" - }, - "schema": { - "type": "string", - "minLength": 1 - }, - "version": { - "const": "1" - }, - "digest": { - "$ref": "#/$defs/sha256" - } - } - } - }, - "deferred_remote_contract": { - "type": "object", - "additionalProperties": false, - "required": [ - "schema", - "version", - "digest" - ], - "properties": { - "schema": { - "const": "deferred_remote_member_v1" - }, - "version": { - "const": "1" - }, - "digest": { - "$ref": "#/$defs/sha256" - } - } - }, - "telemetry_export": { - "type": "object", - "additionalProperties": false, - "required": [ - "schema", - "digest", - "privacy_validation" - ], - "properties": { - "schema": { - "const": "workbundle_stage_event_v1" - }, - "digest": { - "$ref": "#/$defs/sha256" - }, - "privacy_validation": { - "type": "object", - "additionalProperties": false, - "required": [ - "command", - "exit_code", - "output_digest" - ], - "properties": { - "command": { - "type": "string", - "minLength": 1 - }, - "exit_code": { - "const": 0 - }, - "output_digest": { - "$ref": "#/$defs/sha256" - } - } - } - } - }, - "open_risks": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "risk_id", - "summary", - "owner", - "blocking" - ], - "properties": { - "risk_id": { - "$ref": "#/$defs/id" - }, - "summary": { - "type": "string", - "minLength": 1 - }, - "owner": { - "$ref": "#/$defs/id" - }, - "blocking": { - "type": "boolean" - } - } - } - }, - "excluded_work": { - "type": "array", - "minItems": 4, - "uniqueItems": true, - "items": { - "type": "string", - "minLength": 1 - } - }, - "resume_preconditions": { - "type": "array", - "minItems": 1, - "items": { - "type": "string", - "minLength": 1 - } - }, - "created_at": { - "$ref": "#/$defs/time" - } - } - } - } -} diff --git a/evals/wor105/components/native-transition-record.yaml b/evals/wor105/components/native-transition-record.yaml deleted file mode 100644 index 3380f67..0000000 --- a/evals/wor105/components/native-transition-record.yaml +++ /dev/null @@ -1,27 +0,0 @@ -schema: wor105-native-transition-v1 -issue: WOR-105 -transition_task: task-b06r -enforcement_transition: bootstrap_policy_to_native -source_identity: 9dce5df221485174d6179f713e8b179bbc20567a+repository-evidence-sha256:279f142fad6ea3005370b9eaa82d7c88444032e668309173e9230754b28c40ae -review_path: .work-bundle/orchestration/reviews/WOR-105-task-b06r-kernel-review-accepted.yaml -review_sha256: d638b8959b4db57dd33e072b01428d6ce89f65a9771c05eff26d8e40d4293ebd -accepted_commit: 9dce5df221485174d6179f713e8b179bbc20567a -accepted_tree: 5a1f38355eae8068bab528923e807ce54e6f6fe5 -release_anchor: - commit: cfa089f0d2ed211b98d049eb37bfcdccb8091516 - tree: 12e4a696c3caf991654f0b9ac9ef40594699c8d4 -integrated_validation: {id: VAL-B06R-TEST, result: passed, tests: 300} -handoff_validation: {id: VAL-B06R-IDENTITY, result: passed, adversarial_cases: 5} -participant_handoffs: - task-b01: 057ff7eddb3bbfa07e211a3e35f3046fa2ed68520863a791b1362cf8a47a1a08 - task-b02: f7382e2751cd3d5714530d2b92516a69e29603900061258e1ed133feec3e0f4f - task-b03: 7fef606e4d79f375e7b85be07b8751b06f696fb5e5b0bbf481e388038f19e9f3 - task-b03a: d51c708d5795840b1ed82876cf5853d6b80c114774484d2437100b44acf91b4a - task-b04: b0cd71258ba3e90da28afde93450b59a80c53879fe90e613be87e377fa399064 - task-b05: b847cf7d36421e40f09868136a13161504a3e8e9e50e066dd3586aa72b923468 - task-b01r: d266b6d8729d38022daf394b0d342f364409b426628278494c59523e3ba1734d - task-b04r: d7f0bf334c65031ceb5cf80a944e4b1a0f8de3b6b1fd558884ad3f7057931887 - task-b05r: e9ab2afa4da77eb371da0084d4ad77ba79835967f7d64c70c0300ed235d256a7 -native_scope: subsequent WOR-105 phase-c through phase-f execution only -excluded_work: [WOR-66, WOR-79, WOR-107, work-bundle-mcp mutation] -accepted_at: 2026-09-05 diff --git a/evals/wor105/components/task-f01r12-brief.yaml b/evals/wor105/components/task-f01r12-brief.yaml deleted file mode 100644 index 6fbf9bf..0000000 --- a/evals/wor105/components/task-f01r12-brief.yaml +++ /dev/null @@ -1,106 +0,0 @@ -task_brief: - task_id: task-f01r12 - plan_id: plan-20260904-001-wor105-legacy-stabilization - source_ids: - - REQ-020 - - REQ-021 - - REQ-022 - - AC-005 - - AC-011 - goal: "Refreeze evaluation after final CI-oracle repair" - truth_basis: - purpose: "Preserve evaluation 008 and freeze an exact accepted-plan task-capability post-F01R11 product successor" - as_is_evidence: - - "evaluation 008 immutable raw evidence" - - "accepted F01R11 handoff" - - "accepted plan root and aggregate" - - "compiled F01R12 brief" - - "post-F01R11 tree" - decision_authority: - - "AUTH-001: Every validation-bearing invariant needs a capable current correctly bounded harness observation before closure, with typed first-owner repair." - - "AUTH-004: Compile bounded exact-identity task and review packets without executor knowledge retrieval." - expected_delta: - - "append-only invalidations and deterministic successor" - conflict_status: clear - requirements: - - "REQ-020: Freeze product source, spec/task, instruction closure, fixtures, runner, verifier, semantic schema, and evidence capabilities before acceptance runs; runner cannot grade its own semantics." - - "REQ-021: Store separate product revision/tree, instruction/fixture/runner/verifier, invocation/raw response/raw trace/adjudication/evidence digests, and packaging revision." - - "REQ-022: Evaluator component drift explicitly invalidates affected runs; raw responses/traces remain immutable; packaging-only advance preserves separately bound source observation." - - "AC-005: Evaluator drift invalidates affected runs, raw evidence stays immutable, append-only adjudication obeys frozen schema, and packaging-only advance preserves source-bound observations." - - "AC-011: Final adversarial replay passes all twelve user-specified cases with frozen inputs/runner/verifier/schema and exact source identity." - constraints: [] - interfaces: - consumes: [] - produces: [] - files: - read: - - evals/wor105/freeze-manifest.json - - evals/wor105/results.jsonl - - evals/wor105/run.py - - evals/wor105/verify.py - - .work-bundle/runtime/execution/plan-20260904-001-wor105-legacy-stabilization/task-f01r12/task-brief.yaml - write: - - evals/wor105/freeze-manifest.json - - evals/wor105/results.jsonl - - evals/wor105/components/task-f01r12-brief.yaml - forbidden: [] - methodology: - primary: direct - skills: - - dev-systematic-debugging - allocated_rules: - - id: verification-evidence-before-claim - requirement: "replay evidence is claimed" - executor_profile: - capability: judgment - context_mode: compiled-brief - review_capability: judgment - escalation: - after_failed_repairs: 2 - next_capability: judgment - evidence_applicability: - metadata: - required: false - reasons: [] - repository: - required: true - reasons: - - source-inspection - - source-analysis - codegraph: - required: true - reasons: - - source-inspection - - source-analysis - workspace: - root: /Users/shenglong/Documents/Repository/work-bundle-workspace/.worktrees/wor105 - validation: - - kind: process - id: VAL-F01R12 - invariant_ids: - - INV-F01R12 - capability_reason: "two native runs independent verification and exact digest comparison reject drift and nondeterminism" - command: "uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/run.py --manifest evals/wor105/freeze-manifest.json --output evals/wor105/results.jsonl && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/verify.py --manifest evals/wor105/freeze-manifest.json --results evals/wor105/results.jsonl && first=$(sha256sum evals/wor105/results.jsonl | awk '{print $1}') && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/run.py --manifest evals/wor105/freeze-manifest.json --output evals/wor105/results.jsonl && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/verify.py --manifest evals/wor105/freeze-manifest.json --results evals/wor105/results.jsonl && second=$(sha256sum evals/wor105/results.jsonl | awk '{print $1}') && test \"$first\" = \"$second\"" - proves: "exact current-plan task product deterministic evidence" - expected: "two accepted 12-case runs and identical digest" - evidence_capability: - result: mapped - reason: "Exact identities and a double-run verifier close the successor freeze." - invariants: - - id: INV-F01R12 - source_ids: - - REQ-020 - - REQ-021 - - REQ-022 - - AC-005 - invariant: "Final replay is exact and deterministic" - boundary: integration - oracle: VAL-F01R12 - capability_reason: "double-run native verifier" - freshness: current_task_batch - task_id: task-f01r12 - evidence_ids: - - VAL-F01R12 - closure_result: pending - handoff_contract: executor-result-v1 - review_required: false diff --git a/evals/wor105/components/task-f01r15-brief.yaml b/evals/wor105/components/task-f01r15-brief.yaml deleted file mode 100644 index 7b156f3..0000000 --- a/evals/wor105/components/task-f01r15-brief.yaml +++ /dev/null @@ -1,106 +0,0 @@ -task_brief: - task_id: task-f01r15 - plan_id: plan-20260904-001-wor105-legacy-stabilization - source_ids: - - REQ-020 - - REQ-021 - - REQ-022 - - AC-005 - - AC-011 - goal: "Refreeze evaluation after canonical release-gate repair" - truth_basis: - purpose: "Preserve evaluation 009 and freeze an exact accepted-plan task-capability post-F01R14 product successor" - as_is_evidence: - - "evaluation 009 immutable raw evidence" - - "accepted F01R14 handoff" - - "accepted plan root and aggregate" - - "compiled F01R15 brief" - - "post-F01R14 tree" - decision_authority: - - "AUTH-001: Every validation-bearing invariant needs a capable current correctly bounded harness observation before closure, with typed first-owner repair." - - "AUTH-004: Compile bounded exact-identity task and review packets without executor knowledge retrieval." - expected_delta: - - "append-only invalidations and deterministic successor" - conflict_status: clear - requirements: - - "REQ-020: Freeze product source, spec/task, instruction closure, fixtures, runner, verifier, semantic schema, and evidence capabilities before acceptance runs; runner cannot grade its own semantics." - - "REQ-021: Store separate product revision/tree, instruction/fixture/runner/verifier, invocation/raw response/raw trace/adjudication/evidence digests, and packaging revision." - - "REQ-022: Evaluator component drift explicitly invalidates affected runs; raw responses/traces remain immutable; packaging-only advance preserves separately bound source observation." - - "AC-005: Evaluator drift invalidates affected runs, raw evidence stays immutable, append-only adjudication obeys frozen schema, and packaging-only advance preserves source-bound observations." - - "AC-011: Final adversarial replay passes all twelve user-specified cases with frozen inputs/runner/verifier/schema and exact source identity." - constraints: [] - interfaces: - consumes: [] - produces: [] - files: - read: - - evals/wor105/freeze-manifest.json - - evals/wor105/results.jsonl - - evals/wor105/run.py - - evals/wor105/verify.py - - .work-bundle/runtime/execution/plan-20260904-001-wor105-legacy-stabilization/task-f01r15/task-brief.yaml - write: - - evals/wor105/freeze-manifest.json - - evals/wor105/results.jsonl - - evals/wor105/components/task-f01r15-brief.yaml - forbidden: [] - methodology: - primary: direct - skills: - - dev-systematic-debugging - allocated_rules: - - id: verification-evidence-before-claim - requirement: "replay evidence is claimed" - executor_profile: - capability: judgment - context_mode: compiled-brief - review_capability: judgment - escalation: - after_failed_repairs: 2 - next_capability: judgment - evidence_applicability: - metadata: - required: false - reasons: [] - repository: - required: true - reasons: - - source-inspection - - source-analysis - codegraph: - required: true - reasons: - - source-inspection - - source-analysis - workspace: - root: /Users/shenglong/Documents/Repository/work-bundle-workspace/.worktrees/wor105 - validation: - - kind: process - id: VAL-F01R15 - invariant_ids: - - INV-F01R15 - capability_reason: "two native runs independent verification and exact digest comparison reject drift and nondeterminism" - command: "uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/run.py --manifest evals/wor105/freeze-manifest.json --output evals/wor105/results.jsonl && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/verify.py --manifest evals/wor105/freeze-manifest.json --results evals/wor105/results.jsonl && first=$(sha256sum evals/wor105/results.jsonl | awk '{print $1}') && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/run.py --manifest evals/wor105/freeze-manifest.json --output evals/wor105/results.jsonl && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/verify.py --manifest evals/wor105/freeze-manifest.json --results evals/wor105/results.jsonl && second=$(sha256sum evals/wor105/results.jsonl | awk '{print $1}') && test \"$first\" = \"$second\"" - proves: "exact current-plan task product deterministic evidence" - expected: "two accepted 12-case runs and identical digest" - evidence_capability: - result: mapped - reason: "Exact identities and a double-run verifier close the successor freeze." - invariants: - - id: INV-F01R15 - source_ids: - - REQ-020 - - REQ-021 - - REQ-022 - - AC-005 - invariant: "Final replay is exact and deterministic" - boundary: integration - oracle: VAL-F01R15 - capability_reason: "double-run native verifier" - freshness: current_task_batch - task_id: task-f01r15 - evidence_ids: - - VAL-F01R15 - closure_result: pending - handoff_contract: executor-result-v1 - review_required: false diff --git a/evals/wor105/components/task-f01r18-brief.yaml b/evals/wor105/components/task-f01r18-brief.yaml deleted file mode 100644 index ab9d74b..0000000 --- a/evals/wor105/components/task-f01r18-brief.yaml +++ /dev/null @@ -1,106 +0,0 @@ -task_brief: - task_id: task-f01r18 - plan_id: plan-20260904-001-wor105-legacy-stabilization - source_ids: - - REQ-020 - - REQ-021 - - REQ-022 - - AC-005 - - AC-011 - goal: "Refreeze evaluation after historical provenance hydration" - truth_basis: - purpose: "Preserve evaluation 010 and freeze an exact accepted-plan task-capability post-F01R17 product successor" - as_is_evidence: - - "evaluation 010 immutable raw evidence" - - "accepted F01R17 handoff" - - "accepted plan root and aggregate" - - "compiled F01R18 brief" - - "post-F01R17 tree" - decision_authority: - - "AUTH-001: Every validation-bearing invariant needs a capable current correctly bounded harness observation before closure, with typed first-owner repair." - - "AUTH-004: Compile bounded exact-identity task and review packets without executor knowledge retrieval." - expected_delta: - - "append-only invalidations and deterministic successor" - conflict_status: clear - requirements: - - "REQ-020: Freeze product source, spec/task, instruction closure, fixtures, runner, verifier, semantic schema, and evidence capabilities before acceptance runs; runner cannot grade its own semantics." - - "REQ-021: Store separate product revision/tree, instruction/fixture/runner/verifier, invocation/raw response/raw trace/adjudication/evidence digests, and packaging revision." - - "REQ-022: Evaluator component drift explicitly invalidates affected runs; raw responses/traces remain immutable; packaging-only advance preserves separately bound source observation." - - "AC-005: Evaluator drift invalidates affected runs, raw evidence stays immutable, append-only adjudication obeys frozen schema, and packaging-only advance preserves source-bound observations." - - "AC-011: Final adversarial replay passes all twelve user-specified cases with frozen inputs/runner/verifier/schema and exact source identity." - constraints: [] - interfaces: - consumes: [] - produces: [] - files: - read: - - evals/wor105/freeze-manifest.json - - evals/wor105/results.jsonl - - evals/wor105/run.py - - evals/wor105/verify.py - - .work-bundle/runtime/execution/plan-20260904-001-wor105-legacy-stabilization/task-f01r18/task-brief.yaml - write: - - evals/wor105/freeze-manifest.json - - evals/wor105/results.jsonl - - evals/wor105/components/task-f01r18-brief.yaml - forbidden: [] - methodology: - primary: direct - skills: - - dev-systematic-debugging - allocated_rules: - - id: verification-evidence-before-claim - requirement: "replay evidence is claimed" - executor_profile: - capability: judgment - context_mode: compiled-brief - review_capability: judgment - escalation: - after_failed_repairs: 2 - next_capability: judgment - evidence_applicability: - metadata: - required: false - reasons: [] - repository: - required: true - reasons: - - source-inspection - - source-analysis - codegraph: - required: true - reasons: - - source-inspection - - source-analysis - workspace: - root: /Users/shenglong/Documents/Repository/work-bundle-workspace/.worktrees/wor105 - validation: - - kind: process - id: VAL-F01R18 - invariant_ids: - - INV-F01R18 - capability_reason: "two native runs independent verification and exact digest comparison reject drift and nondeterminism" - command: "uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/run.py --manifest evals/wor105/freeze-manifest.json --output evals/wor105/results.jsonl && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/verify.py --manifest evals/wor105/freeze-manifest.json --results evals/wor105/results.jsonl && first=$(sha256sum evals/wor105/results.jsonl | awk '{print $1}') && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/run.py --manifest evals/wor105/freeze-manifest.json --output evals/wor105/results.jsonl && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/verify.py --manifest evals/wor105/freeze-manifest.json --results evals/wor105/results.jsonl && second=$(sha256sum evals/wor105/results.jsonl | awk '{print $1}') && test \"$first\" = \"$second\"" - proves: "exact current-plan task product deterministic evidence" - expected: "two accepted 12-case runs and identical digest" - evidence_capability: - result: mapped - reason: "Exact identities and a double-run verifier close the successor freeze." - invariants: - - id: INV-F01R18 - source_ids: - - REQ-020 - - REQ-021 - - REQ-022 - - AC-005 - invariant: "Final replay is exact and deterministic" - boundary: integration - oracle: VAL-F01R18 - capability_reason: "double-run native verifier" - freshness: current_task_batch - task_id: task-f01r18 - evidence_ids: - - VAL-F01R18 - closure_result: pending - handoff_contract: executor-result-v1 - review_required: false diff --git a/evals/wor105/components/task-f01r2-brief.yaml b/evals/wor105/components/task-f01r2-brief.yaml deleted file mode 100644 index 06dca45..0000000 --- a/evals/wor105/components/task-f01r2-brief.yaml +++ /dev/null @@ -1,100 +0,0 @@ -task_brief: - task_id: task-f01r2 - plan_id: plan-20260904-001-wor105-legacy-stabilization - source_ids: - - AC-005 - - AC-011 - - AC-012 - goal: "Refreeze evaluation after release-oracle plan repair" - truth_basis: - purpose: "Append invalidations for every frozen identity changed by the release CI repairs and produce a fresh deterministic native evaluation" - as_is_evidence: - - "task-f01r4 accepted CI-oracle repair" - - "task-f01r5 accepted clean-checkout evaluator repair" - - "task-f01r6 accepted sandbox-status repair" - - "evaluation 006" - - "accepted repaired plan identity" - decision_authority: - - "AUTH-001: Every validation-bearing invariant needs a capable current correctly bounded harness observation before closure, with typed first-owner repair." - - "AUTH-006: Decide every material relation with explicit stopping and no silent expansion." - expected_delta: - - "evaluation 006 remains invalidated for plan tasks evidence-capability and post-F01R6 product-tree drift" - - "fresh manifest binds the accepted root plan digest F01R2 task brief and post-F01R6 product tree" - - "fresh 12/12 result is recorded" - conflict_status: clear - requirements: - - "AC-005: Evaluator drift invalidates affected runs, raw evidence stays immutable, append-only adjudication obeys frozen schema, and packaging-only advance preserves source-bound observations." - - "AC-011: Final adversarial replay passes all twelve user-specified cases with frozen inputs/runner/verifier/schema and exact source identity." - - "AC-012: Full pinned suite, behavior evals, Ubuntu/macOS CI, exact final-tree independent review, knowledge closure, release identity, and migration handoff pass before completion." - constraints: [] - interfaces: - consumes: [] - produces: [] - files: - read: - - evals/wor105/freeze-manifest.json - - evals/wor105/results.jsonl - - evals/wor105/run.py - - evals/wor105/verify.py - write: - - evals/wor105/freeze-manifest.json - - evals/wor105/results.jsonl - forbidden: [] - methodology: - primary: direct - skills: - - dev-systematic-debugging - allocated_rules: - - id: verification-evidence-before-claim - requirement: "release refreeze is claimed" - executor_profile: - capability: judgment - context_mode: compiled-brief - review_capability: judgment - escalation: - after_failed_repairs: 2 - next_capability: judgment - evidence_applicability: - metadata: - required: false - reasons: [] - repository: - required: true - reasons: - - source-inspection - - source-analysis - codegraph: - required: true - reasons: - - source-inspection - - source-analysis - workspace: - root: /Users/shenglong/Documents/Repository/work-bundle-workspace/.worktrees/wor105 - validation: - - kind: process - id: VAL-F01R2 - invariant_ids: - - INV-F01R2 - capability_reason: "two native runs independent verification and exact digest equality bind all changed frozen identities to deterministic observations" - command: "uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/run.py --manifest evals/wor105/freeze-manifest.json --output evals/wor105/results.jsonl && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/verify.py --manifest evals/wor105/freeze-manifest.json --results evals/wor105/results.jsonl && first=$(sha256sum evals/wor105/results.jsonl | awk '{print $1}') && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/run.py --manifest evals/wor105/freeze-manifest.json --output evals/wor105/results.jsonl && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/verify.py --manifest evals/wor105/freeze-manifest.json --results evals/wor105/results.jsonl && second=$(sha256sum evals/wor105/results.jsonl | awk '{print $1}') && test \"$first\" = \"$second\"" - proves: "current-plan current-task current-product deterministic native evaluation identity" - expected: "both 12/12 passed and digests equal" - evidence_capability: - result: mapped - reason: "Fresh deterministic replay closes plan task evidence-capability and product identity drift." - invariants: - - id: INV-F01R2 - source_ids: - - AC-005 - - AC-011 - invariant: "Plan task evidence-capability and post-F01R6 product drift invalidate evaluation 006 and a fully current native evaluation passes 12/12 deterministically" - boundary: integration - oracle: VAL-F01R2 - capability_reason: "two native replays independent verification and exact result digest equality" - freshness: current_task_batch - task_id: task-f01r2 - evidence_ids: - - VAL-F01R2 - closure_result: pending - handoff_contract: executor-result-v1 - review_required: false diff --git a/evals/wor105/components/task-f01r25-brief.yaml b/evals/wor105/components/task-f01r25-brief.yaml deleted file mode 100644 index 3d58e83..0000000 --- a/evals/wor105/components/task-f01r25-brief.yaml +++ /dev/null @@ -1,141 +0,0 @@ -task_brief: - task_id: task-f01r25 - plan_id: plan-20260904-001-wor105-legacy-stabilization - source_ids: - - REQ-020 - - REQ-021 - - REQ-022 - - REQ-032 - - AC-005 - - AC-011 - - AC-012 - goal: "Refreeze and locally accept the evidence-reuse release candidate" - truth_basis: - purpose: "Refresh exact evaluation and local release acceptance after the user-directed observation reuse repair" - as_is_evidence: - - "evaluation 011 preserved in Git" - - "accepted F01R24 handoff" - - "commit e4e1e3b" - - "current independent plan review" - decision_authority: - - "AUTH-001: Every validation-bearing invariant needs a capable current correctly bounded harness observation before closure, with typed first-owner repair." - - "AUTH-002: Keep ordinary task review applicability-based, route failures to the first broken artifact, and keep final review a workflow audit." - - "AUTH-004: Compile bounded exact-identity task and review packets without executor knowledge retrieval." - expected_delta: - - "append-only evaluation 012" - - "deterministic native replay" - - "one current and tracked-only full-gate pair" - - "reusable unchanged validation" - conflict_status: clear - requirements: - - "REQ-020: Freeze product source, spec/task, instruction closure, fixtures, runner, verifier, semantic schema, and evidence capabilities before acceptance runs; runner cannot grade its own semantics." - - "REQ-021: Store separate product revision/tree, instruction/fixture/runner/verifier, invocation/raw response/raw trace/adjudication/evidence digests, and packaging revision." - - "REQ-022: Evaluator component drift explicitly invalidates affected runs; raw responses/traces remain immutable; packaging-only advance preserves separately bound source observation." - - "REQ-032: Reuse passed deterministic validation through the existing evidence/provenance model only when complete claim-relevant source/input, semantic check, runner/fixture, environment/binding, freshness-policy and explicit revocation identities match. Keep conservative tracked plus relevant task-created/dirty/untracked source coverage; exclude runtime receipts, handoffs, logs and other declared observation outputs from source identity, without excluding declared inputs. Check identity includes id/kind, command/mechanism, expected/acceptable results and invariant IDs. Environment identity binds relevant OS/architecture, runtime/dependencies/profile, cwd and explicit environment inputs, never the whole environment or volatile temporary paths. Structural task/plan/handoff identity, binding/write scope, result shape, knowledge disposition, closure and authorization checks always run. Failed and skipped observations are not reusable positive evidence; live checks rerun unless explicitly bounded by freshness. Complete deterministic A → B → A restoration may reuse original fresh evidence; explicit revocation still invalidates. Local observations cannot satisfy required GitHub Ubuntu/macOS evidence without accepted explicit environment equivalence. No inferred per-feature dependency-closure optimization." - - "AC-005: Evaluator drift invalidates affected runs, raw evidence stays immutable, append-only adjudication obeys frozen schema, and packaging-only advance preserves source-bound observations." - - "AC-011: Final adversarial replay passes all twelve user-specified cases with frozen inputs/runner/verifier/schema and exact source identity." - - "AC-012: Full pinned suite, behavior evals, Ubuntu/macOS CI, exact final-tree independent review, knowledge closure, release identity, and migration handoff pass before completion." - constraints: [] - interfaces: - consumes: [] - produces: [] - files: - read: - - evals/wor105/freeze-manifest.json - - evals/wor105/results.jsonl - - evals/wor105/run.py - - evals/wor105/verify.py - - .work-bundle/runtime/execution/plan-20260904-001-wor105-legacy-stabilization/task-f01r25/task-brief.yaml - write: - - evals/wor105/freeze-manifest.json - - evals/wor105/results.jsonl - - evals/wor105/components/task-f01r25-brief.yaml - forbidden: [] - methodology: - primary: direct - skills: - - dev-systematic-debugging - allocated_rules: - - id: verification-evidence-before-claim - requirement: "replay evidence is claimed" - executor_profile: - capability: judgment - context_mode: compiled-brief - review_capability: judgment - escalation: - after_failed_repairs: 2 - next_capability: judgment - evidence_applicability: - metadata: - required: false - reasons: [] - repository: - required: true - reasons: - - source-inspection - - source-analysis - codegraph: - required: true - reasons: - - source-inspection - - source-analysis - workspace: - root: /Users/shenglong/Documents/Repository/work-bundle-workspace/.worktrees/wor105 - validation: - - kind: process - evidence_reuse: - mode: deterministic - max_age_seconds: 86400 - environment_inputs: [] - dependency_files: [] - output_paths: [] - profile: canonical-pinned-release-gate - include_head: true - id: VAL-F01R25 - invariant_ids: - - INV-F01R25 - capability_reason: "two native runs independent verification and exact digest comparison reject drift and nondeterminism" - command: "uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/run.py --manifest evals/wor105/freeze-manifest.json --output evals/wor105/results.jsonl && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/verify.py --manifest evals/wor105/freeze-manifest.json --results evals/wor105/results.jsonl && first=$(sha256sum evals/wor105/results.jsonl | awk '{print $1}') && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/run.py --manifest evals/wor105/freeze-manifest.json --output evals/wor105/results.jsonl && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/verify.py --manifest evals/wor105/freeze-manifest.json --results evals/wor105/results.jsonl && second=$(sha256sum evals/wor105/results.jsonl | awk '{print $1}') && test \"$first\" = \"$second\"" - proves: "exact current-plan task product deterministic evidence" - expected: "two accepted 12-case runs and identical digest" - - kind: process - evidence_reuse: - mode: deterministic - max_age_seconds: 86400 - environment_inputs: [] - dependency_files: [] - output_paths: [] - profile: canonical-pinned-release-gate - include_head: true - id: VAL-F01R25-CLEAN - invariant_ids: - - INV-F01R25 - capability_reason: "ordered current and tracked-only gates compare nonempty exact complete summaries and fail on any difference" - command: "bash -c 'set -euo pipefail; tmp=$(mktemp -d /tmp/wor105-release.XXXXXX); trap '\\''git worktree remove \"$tmp\" >/dev/null 2>&1 || true'\\'' EXIT; git worktree add --detach \"$tmp\" HEAD >/dev/null; if current=$(bin/work-bundle-ci); then :; else printf \"%s\\n\" \"$current\"; exit 1; fi; if clean=$(\"$tmp/bin/work-bundle-ci\"); then :; else printf \"%s\\n\" \"$clean\"; exit 1; fi; current_summary=$(printf \"%s\\n\" \"$current\" | grep ^WB_CI_); clean_summary=$(printf \"%s\\n\" \"$clean\" | grep ^WB_CI_); test -n \"$current_summary\"; test -n \"$clean_summary\"; test \"$current_summary\" = \"$clean_summary\"; printf \"%s\\n\" \"$clean_summary\"'" - proves: "exact current and tracked-only full-gate equality" - expected: "passed with identical nonempty module list and verdict" - evidence_capability: - result: mapped - reason: "Exact replay plus ordered complete release gates validate this final candidate once." - invariants: - - id: INV-F01R25 - source_ids: - - REQ-020 - - REQ-021 - - REQ-022 - - REQ-032 - - AC-005 - - AC-011 - - AC-012 - invariant: "Current deterministic replay and complete portable release verdict share the accepted candidate" - boundary: integration - oracle: VAL-F01R25-CLEAN - capability_reason: "double replay verifier plus current and tracked-only gate equality" - freshness: current_task_batch - task_id: task-f01r25 - evidence_ids: - - VAL-F01R25 - - VAL-F01R25-CLEAN - closure_result: pending - handoff_contract: executor-result-v1 - review_required: false diff --git a/evals/wor105/components/task-f01r9-brief.yaml b/evals/wor105/components/task-f01r9-brief.yaml deleted file mode 100644 index bdafd93..0000000 --- a/evals/wor105/components/task-f01r9-brief.yaml +++ /dev/null @@ -1,106 +0,0 @@ -task_brief: - task_id: task-f01r9 - plan_id: plan-20260904-001-wor105-legacy-stabilization - source_ids: - - REQ-020 - - REQ-021 - - REQ-022 - - AC-005 - - AC-011 - goal: "Preserve evaluation 007 and freeze a deterministic successor bound to the accepted root-plan digest, compiled F01R9 task-brief digest, and post-F01R8 product tree." - truth_basis: - purpose: "Invalidate evaluation 007 for accepted plan task-capability and product drift and freeze its successor" - as_is_evidence: - - "evaluation 007 immutable raw evidence" - - "accepted F01R7 and F01R8 repair handoffs" - - "freshly accepted exact root-plan and aggregate digests" - - "compiled F01R9 task brief" - - "post-F01R8 source tree" - decision_authority: - - "AUTH-001: Every validation-bearing invariant needs a capable current correctly bounded harness observation before closure, with typed first-owner repair." - - "AUTH-004: Compile bounded exact-identity task and review packets without executor knowledge retrieval." - expected_delta: - - "append-only invalidations and deterministic exact-plan exact-task exact-product successor evaluation" - conflict_status: clear - requirements: - - "REQ-020: Freeze product source, spec/task, instruction closure, fixtures, runner, verifier, semantic schema, and evidence capabilities before acceptance runs; runner cannot grade its own semantics." - - "REQ-021: Store separate product revision/tree, instruction/fixture/runner/verifier, invocation/raw response/raw trace/adjudication/evidence digests, and packaging revision." - - "REQ-022: Evaluator component drift explicitly invalidates affected runs; raw responses/traces remain immutable; packaging-only advance preserves separately bound source observation." - - "AC-005: Evaluator drift invalidates affected runs, raw evidence stays immutable, append-only adjudication obeys frozen schema, and packaging-only advance preserves source-bound observations." - - "AC-011: Final adversarial replay passes all twelve user-specified cases with frozen inputs/runner/verifier/schema and exact source identity." - constraints: [] - interfaces: - consumes: [] - produces: [] - files: - read: - - evals/wor105/freeze-manifest.json - - evals/wor105/results.jsonl - - evals/wor105/run.py - - evals/wor105/verify.py - - .work-bundle/runtime/execution/plan-20260904-001-wor105-legacy-stabilization/task-f01r9/task-brief.yaml - write: - - evals/wor105/freeze-manifest.json - - evals/wor105/results.jsonl - - evals/wor105/components/task-f01r9-brief.yaml - forbidden: [] - methodology: - primary: direct - skills: - - dev-systematic-debugging - allocated_rules: - - id: verification-evidence-before-claim - requirement: "current replay evidence is claimed" - executor_profile: - capability: judgment - context_mode: compiled-brief - review_capability: judgment - escalation: - after_failed_repairs: 2 - next_capability: judgment - evidence_applicability: - metadata: - required: false - reasons: [] - repository: - required: true - reasons: - - source-inspection - - source-analysis - codegraph: - required: true - reasons: - - source-inspection - - source-analysis - workspace: - root: /Users/shenglong/Documents/Repository/work-bundle-workspace/.worktrees/wor105 - validation: - - kind: process - id: VAL-F01R9 - invariant_ids: - - INV-F01R9 - capability_reason: "two native runs plus independent verification and exact digest comparison detect nondeterminism stale identity and failed cases" - command: "uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/run.py --manifest evals/wor105/freeze-manifest.json --output evals/wor105/results.jsonl && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/verify.py --manifest evals/wor105/freeze-manifest.json --results evals/wor105/results.jsonl && first=$(sha256sum evals/wor105/results.jsonl | awk '{print $1}') && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/run.py --manifest evals/wor105/freeze-manifest.json --output evals/wor105/results.jsonl && uvx --python 3.13 --from pyyaml==6.0.3 python evals/wor105/verify.py --manifest evals/wor105/freeze-manifest.json --results evals/wor105/results.jsonl && second=$(sha256sum evals/wor105/results.jsonl | awk '{print $1}') && test \"$first\" = \"$second\"" - proves: "exact current-plan current-task current-product deterministic 12-case native evidence" - expected: "both runs pass 12 of 12 and result digests are identical" - evidence_capability: - result: mapped - reason: "Successor freeze and repeated independent verification reject stale or nondeterministic evidence." - invariants: - - id: INV-F01R9 - source_ids: - - REQ-020 - - REQ-021 - - REQ-022 - - AC-005 - invariant: "Release replay evidence names exact current components and remains deterministic" - boundary: integration - oracle: VAL-F01R9 - capability_reason: "native double-run and verifier" - freshness: current_task_batch - task_id: task-f01r9 - evidence_ids: - - VAL-F01R9 - closure_result: pending - handoff_contract: executor-result-v1 - review_required: false diff --git a/evals/wor105/fixtures/ADV-01.json b/evals/wor105/fixtures/ADV-01.json deleted file mode 100644 index f8ac5c7..0000000 --- a/evals/wor105/fixtures/ADV-01.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "fixture_id": "ADV-01", - "input": { - "operation": "review_access_probe", - "writes": [ - "source_sentinel", - "control_sentinel" - ], - "protected_reads": [ - "live_registry", - "workspace_credentials", - "host_config" - ], - "allowed": [ - "read_target", - "run_bounded_validator" - ] - }, - "expected_decision": "deny_mutation_and_protected_reads_allow_bounded_evidence", - "proof_required": [ - "source_sentinel_before_sha256", - "source_sentinel_after_sha256", - "control_sentinel_before_sha256", - "control_sentinel_after_sha256", - "denial_classes", - "allowed_read_output_sha256", - "validator_output_sha256", - "event_ids" - ] -} diff --git a/evals/wor105/fixtures/ADV-02.json b/evals/wor105/fixtures/ADV-02.json deleted file mode 100644 index 18a6149..0000000 --- a/evals/wor105/fixtures/ADV-02.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "fixture_id": "ADV-02", - "input": { - "task_id": "fixture-task", - "original_scope": [ - "owned-a" - ], - "original_evidence_ids": [ - "E-1" - ], - "expansions": [ - [ - "owned-b" - ], - [ - "owned-c" - ] - ] - }, - "expected_decision": "pause_and_reslice_after_second_expansion", - "proof_required": [ - "original_evidence_sha256", - "expansion_event_ids", - "binding_state", - "reslice_artifact_sha256", - "return_owner" - ] -} diff --git a/evals/wor105/fixtures/ADV-03.json b/evals/wor105/fixtures/ADV-03.json deleted file mode 100644 index fd8279b..0000000 --- a/evals/wor105/fixtures/ADV-03.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "fixture_id": "ADV-03", - "input": { - "finding": { - "class": "implementation_defect", - "first_broken_artifact": "implementation", - "recommended_owner": "task_owner", - "disposition": "repair_task" - }, - "known_fact": "missing_plan_allocation" - }, - "expected_decision": "reject_and_route_allocation_gap_to_plan_reslice", - "proof_required": [ - "rejected_record_sha256", - "canonical_finding_sha256", - "validation_error_code" - ] -} diff --git a/evals/wor105/fixtures/ADV-04.json b/evals/wor105/fixtures/ADV-04.json deleted file mode 100644 index 178f696..0000000 --- a/evals/wor105/fixtures/ADV-04.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "fixture_id": "ADV-04", - "input": { - "class": "advisory_enhancement", - "severity": "blocking", - "obligation_basis": "none", - "evidence": [] - }, - "expected_decision": "reject_blocking_and_record_nonblocking_advisory", - "proof_required": [ - "validation_error_code", - "advisory_id", - "stage_state_before_sha256", - "stage_state_after_sha256" - ] -} diff --git a/evals/wor105/fixtures/ADV-05.json b/evals/wor105/fixtures/ADV-05.json deleted file mode 100644 index 01f18fb..0000000 --- a/evals/wor105/fixtures/ADV-05.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "fixture_id": "ADV-05", - "input": { - "run_status": "valid", - "changed_component": "fixture", - "invalidation_present": false - }, - "expected_decision": "stale_run_append_invalidation_preserve_raw_evidence", - "proof_required": [ - "old_digest", - "new_digest", - "stale_run_id", - "invalidation_id", - "raw_response_before_sha256", - "raw_response_after_sha256", - "raw_trace_before_sha256", - "raw_trace_after_sha256" - ] -} diff --git a/evals/wor105/fixtures/ADV-06.json b/evals/wor105/fixtures/ADV-06.json deleted file mode 100644 index 723ca83..0000000 --- a/evals/wor105/fixtures/ADV-06.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "fixture_id": "ADV-06", - "input": { - "product_tree": "1111111111111111111111111111111111111111", - "packaging_before": "2222222222222222222222222222222222222222", - "packaging_after": "3333333333333333333333333333333333333333" - }, - "expected_decision": "preserve_product_observation_update_packaging_only", - "proof_required": [ - "product_tree_before", - "product_tree_after", - "observation_before_sha256", - "observation_after_sha256", - "packaging_before", - "packaging_after", - "valid" - ] -} diff --git a/evals/wor105/fixtures/ADV-07.json b/evals/wor105/fixtures/ADV-07.json deleted file mode 100644 index 96be942..0000000 --- a/evals/wor105/fixtures/ADV-07.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "fixture_id": "ADV-07", - "input": { - "review_verdict": "accepted", - "target_before_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "target_after_sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" - }, - "expected_decision": "mark_review_stale_and_remove_stage_credit", - "proof_required": [ - "review_id", - "target_before_sha256", - "target_after_sha256", - "staleness_reason", - "countable_stage_reviews" - ] -} diff --git a/evals/wor105/fixtures/ADV-08.json b/evals/wor105/fixtures/ADV-08.json deleted file mode 100644 index d39c989..0000000 --- a/evals/wor105/fixtures/ADV-08.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "fixture_id": "ADV-08", - "input": { - "requests": 2, - "identity": { - "command_digest": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", - "cwd_token": "isolated_execution_root", - "product_tree": "1111111111111111111111111111111111111111", - "state_digest": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", - "oracle_digest": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", - "mutation_epoch": 0 - } - }, - "expected_decision": "execute_once_and_reuse_observation", - "proof_required": [ - "request_ids", - "subprocess_invocation_count", - "observation_id", - "reuse_of" - ] -} diff --git a/evals/wor105/fixtures/ADV-09.json b/evals/wor105/fixtures/ADV-09.json deleted file mode 100644 index 2c3c70e..0000000 --- a/evals/wor105/fixtures/ADV-09.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "fixture_id": "ADV-09", - "input": { - "binding_states": [ - "repair_owned", - "rereview_owned" - ], - "operation": "release" - }, - "expected_decision": "deny_release_preserve_owner_reason_history", - "proof_required": [ - "before_snapshot_sha256", - "after_snapshot_sha256", - "denial_event_ids", - "original_owner", - "original_reason" - ] -} diff --git a/evals/wor105/fixtures/ADV-10.json b/evals/wor105/fixtures/ADV-10.json deleted file mode 100644 index efd0060..0000000 --- a/evals/wor105/fixtures/ADV-10.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "fixture_id": "ADV-10", - "input": { - "otherwise_valid": true, - "participation_trials": [ - "authorship", - "repair_participation", - "decision_participation", - "deliberation_participation" - ] - }, - "expected_decision": "reject_each_and_require_fresh_reviewer", - "proof_required": [ - "validation_error_codes", - "rejected_review_ids", - "countable_stage_reviews" - ] -} diff --git a/evals/wor105/fixtures/ADV-11.json b/evals/wor105/fixtures/ADV-11.json deleted file mode 100644 index 96b6348..0000000 --- a/evals/wor105/fixtures/ADV-11.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "fixture_id": "ADV-11", - "input": { - "extension_authorized": true, - "public_contract_satisfied": true, - "fixture_bytes_changed": true, - "oracle": "byte_equality" - }, - "expected_decision": "route_validation_oracle_defect_without_product_rollback", - "proof_required": [ - "public_contract_test_output_sha256", - "byte_oracle_failure_sha256", - "routed_finding_sha256", - "product_revision_before", - "product_revision_after" - ] -} diff --git a/evals/wor105/fixtures/ADV-12.json b/evals/wor105/fixtures/ADV-12.json deleted file mode 100644 index fad2c45..0000000 --- a/evals/wor105/fixtures/ADV-12.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "fixture_id": "ADV-12", - "input": { - "placeholder_remote": "dummy://placeholder", - "canonical_remote": null, - "materialization": "deferred", - "apply_count": 2 - }, - "expected_decision": "reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop", - "proof_required": [ - "validation_error_code", - "member_snapshot_sha256", - "checkout_absent", - "origin_absent", - "first_apply_state_sha256", - "replay_state_sha256" - ] -} diff --git a/evals/wor105/freeze-manifest.json b/evals/wor105/freeze-manifest.json deleted file mode 100644 index 5a190df..0000000 --- a/evals/wor105/freeze-manifest.json +++ /dev/null @@ -1,504 +0,0 @@ -{ - "schema": "wor105-adversarial-freeze-v1", - "evaluation_id": "wor105-native-adversarial-20260905-012", - "frozen_at": "2026-09-05T12:19:01Z", - "enforcement_mode": "native", - "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", - "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", - "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", - "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization", - "fixtures": [ - { - "fixture_id": "ADV-01", - "path": "evals/wor105/fixtures/ADV-01.json", - "sha256": "52e6a3f83058f820fe01343e5b009940842874f05bdf47aa2e99ea60a27127bb" - }, - { - "fixture_id": "ADV-02", - "path": "evals/wor105/fixtures/ADV-02.json", - "sha256": "78fe6de27c7833336f6883a73dc61aaa0b15b37735ae639e9061e071e0dfcd40" - }, - { - "fixture_id": "ADV-03", - "path": "evals/wor105/fixtures/ADV-03.json", - "sha256": "490ebdd8890adb88bd52a6cd24a54d02bdc6750d0a3f9cc63e78cc6a2d68d260" - }, - { - "fixture_id": "ADV-04", - "path": "evals/wor105/fixtures/ADV-04.json", - "sha256": "19db36abd1deb09f95179b572e2c559509aea0a2607aa1a04cd4b5af7db291ef" - }, - { - "fixture_id": "ADV-05", - "path": "evals/wor105/fixtures/ADV-05.json", - "sha256": "42251cea32fd52341f87067be6bbc4194da7139b65b823dcf230b11f50baa3f7" - }, - { - "fixture_id": "ADV-06", - "path": "evals/wor105/fixtures/ADV-06.json", - "sha256": "f5241e874cd284c3e41ecab7e2790d265a8ca7f0e53785d9bb151ebeefb16d6e" - }, - { - "fixture_id": "ADV-07", - "path": "evals/wor105/fixtures/ADV-07.json", - "sha256": "f9446f50289c62fd1115d89d2121576950174cbad68bb4e76cdd8cef1a962d47" - }, - { - "fixture_id": "ADV-08", - "path": "evals/wor105/fixtures/ADV-08.json", - "sha256": "b8b07a687c7d492ad983b88a72b837f18ae50b6c384aa394a732c60e940bd01e" - }, - { - "fixture_id": "ADV-09", - "path": "evals/wor105/fixtures/ADV-09.json", - "sha256": "5a347c746583f3bc1175b50aa5b2fdf8250e07a929206d777a48bdb7e50ca70f" - }, - { - "fixture_id": "ADV-10", - "path": "evals/wor105/fixtures/ADV-10.json", - "sha256": "7ea520dcefa78d9348f354b1a9dcc9726798a0759b0b768d1ae476770731c673" - }, - { - "fixture_id": "ADV-11", - "path": "evals/wor105/fixtures/ADV-11.json", - "sha256": "4520983615ce735ab5cd6ba1729e464085e0af0ec2f152fcf32872e201041ef7" - }, - { - "fixture_id": "ADV-12", - "path": "evals/wor105/fixtures/ADV-12.json", - "sha256": "7f29996db2939960f308f7bfe72e139e3eded355804a72860b98add5d0ce0e18" - } - ], - "components": { - "profile": { - "path": "evals/wor105/components/native-transition-record.yaml", - "sha256": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72" - }, - "fixtures": { - "path": "evals/wor105/fixtures", - "sha256": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2" - }, - "runner": { - "path": "evals/wor105/run.py", - "sha256": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653" - }, - "verifier": { - "path": "evals/wor105/verify.py", - "sha256": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4" - }, - "result_schema": { - "path": "evals/wor105/adversarial-result-v1.schema.json", - "sha256": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89" - }, - "semantic_schema": { - "path": "evals/wor105/components/contracts-v1.schema.json", - "sha256": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67" - }, - "instructions": { - "path": "AGENTS.md", - "sha256": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a" - }, - "evidence_capabilities": { - "path": "evals/wor105/components/task-f01r25-brief.yaml", - "sha256": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76" - } - }, - "invalidations": [ - { - "invalidation_id": "wor105-eval-invalidation-001", - "previous_evaluation_id": "wor105-native-adversarial-20260905-001", - "changed_component": "runner", - "old_digest": "4e577385985c220bf254969d58e7b861329e46d17b11f2409b0919efd3ad4b0a", - "new_digest": "6367a19b7b462c50b62e034e0ec7aa436fed21c696cf0e23d5a73be0b8eb9351", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-001-attempt-001" - ], - "reason": "ADV-12 runner referenced remote instead of the frozen canonical_remote field", - "timestamp": "2026-09-04T19:30:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-002", - "previous_evaluation_id": "wor105-native-adversarial-20260905-002", - "changed_component": "product", - "old_digest": "9c3e40f234141c3de278136bad5736838042b06945f1d9b78a7625569bb9dc3f", - "new_digest": "af8cf733a29162cbd1b80c8bb03ee95276c03d931c48fe8bcd6be24701e8bb06", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-002-attempt-001" - ], - "reason": "native dogfood test became part of the exact release candidate tree", - "timestamp": "2026-09-04T19:45:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-003", - "previous_evaluation_id": "wor105-native-adversarial-20260905-002", - "changed_component": "tasks", - "old_digest": "45c71ee3a63368ed66bbf3b95379af2de7a617b5467d5abe91f2ac72b4e5a46e", - "new_digest": "a87d554901ccddb5e8af364ea9e3868b267d22f2ac0f900ae62fa91a3b849745", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-002-attempt-001" - ], - "reason": "release full-suite validation oracle was repaired and independently accepted", - "timestamp": "2026-09-04T19:45:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-004", - "previous_evaluation_id": "wor105-native-adversarial-20260905-002", - "changed_component": "evidence_capabilities", - "old_digest": "98426ea693efb41bf1fc7dbeeefb7f34161c2825d7a42deddd5b9aa900f18c57", - "new_digest": "24e530f502f0360ac44e1ccb1870b4e98db0d5715ff8bb69e721a5dd61d85142", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-002-attempt-001" - ], - "reason": "evaluation evidence ownership moved to the explicit plan-refreeze task", - "timestamp": "2026-09-04T19:45:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-005", - "previous_evaluation_id": "wor105-native-adversarial-20260905-003", - "changed_component": "runner", - "old_digest": "6367a19b7b462c50b62e034e0ec7aa436fed21c696cf0e23d5a73be0b8eb9351", - "new_digest": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-003-attempt-001" - ], - "reason": "the prior runner authored proof claims without invoking native WorkBundle operations", - "timestamp": "2026-09-05T06:20:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-006", - "previous_evaluation_id": "wor105-native-adversarial-20260905-003", - "changed_component": "tasks", - "old_digest": "a87d554901ccddb5e8af364ea9e3868b267d22f2ac0f900ae62fa91a3b849745", - "new_digest": "7e37be1260bc3c39c0f2516c43246f5c520444d6c584796a39afb1583ab565ca", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-003-attempt-001" - ], - "reason": "the accepted plan was resliced to allocate native-probe repair and refreeze ownership", - "timestamp": "2026-09-05T06:20:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-007", - "previous_evaluation_id": "wor105-native-adversarial-20260905-003", - "changed_component": "evidence_capabilities", - "old_digest": "24e530f502f0360ac44e1ccb1870b4e98db0d5715ff8bb69e721a5dd61d85142", - "new_digest": "7f600f35963f2edc192452a3de8ca5ce018479ef9280be8167a0e79a894f0514", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-003-attempt-001" - ], - "reason": "evaluation evidence ownership moved to the native-probe refreeze task", - "timestamp": "2026-09-05T06:20:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-008", - "previous_evaluation_id": "wor105-native-adversarial-20260905-003", - "changed_component": "product", - "old_digest": "ec056c774b8aef3d680743e2ea95be8e260f3f06653c06d5bfb89be2c757fcc1", - "new_digest": "55a5d96c95a1783832beaf0e588c0915617cec0fa18f698afa53b8bdabeadca6", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-003-attempt-001" - ], - "reason": "the native-probe runner and zero-call regression entered the exact product tree", - "timestamp": "2026-09-05T06:20:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-009", - "previous_evaluation_id": "wor105-native-adversarial-20260905-004", - "changed_component": "tasks", - "old_digest": "7e37be1260bc3c39c0f2516c43246f5c520444d6c584796a39afb1583ab565ca", - "new_digest": "ae2f0513fd7b38bb751e5f1eacc1d85ede20293c93a7fce3715c2c97e1e876da", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-004-attempt-001" - ], - "reason": "the accepted plan allocated historical transition-oracle repair and its downstream refreeze and dogfood gates", - "timestamp": "2026-09-05T06:45:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-010", - "previous_evaluation_id": "wor105-native-adversarial-20260905-004", - "changed_component": "evidence_capabilities", - "old_digest": "7f600f35963f2edc192452a3de8ca5ce018479ef9280be8167a0e79a894f0514", - "new_digest": "81bba349bff55c80de7a56de624994c3c95f4a7a82a6177131401937a3e72867", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-004-attempt-001" - ], - "reason": "evaluation evidence ownership moved to the release refreeze task", - "timestamp": "2026-09-05T06:45:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-011", - "previous_evaluation_id": "wor105-native-adversarial-20260905-004", - "changed_component": "product", - "old_digest": "55a5d96c95a1783832beaf0e588c0915617cec0fa18f698afa53b8bdabeadca6", - "new_digest": "3b962d0ce3240155c6feafd34a3a3ca2f36a835c5d6c80b2f1ffbeb5c39dbf80", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-004-attempt-001" - ], - "reason": "the corrected historical transition oracle entered the post-F01R1 product tree", - "timestamp": "2026-09-05T06:45:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-012", - "previous_evaluation_id": "wor105-native-adversarial-20260905-005", - "changed_component": "tasks", - "old_digest": "ae2f0513fd7b38bb751e5f1eacc1d85ede20293c93a7fce3715c2c97e1e876da", - "new_digest": "a0f8c6b69de964b4d14c900cfd209d12bcab0daa674510ea5b7eaefa033e14a3", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-005-attempt-001" - ], - "reason": "the accepted plan allocated the exact CI-oracle repair and downstream release gates", - "timestamp": "2026-09-05T07:13:59Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-013", - "previous_evaluation_id": "wor105-native-adversarial-20260905-005", - "changed_component": "evidence_capabilities", - "old_digest": "81bba349bff55c80de7a56de624994c3c95f4a7a82a6177131401937a3e72867", - "new_digest": "e2638e31f2d61c09e8975cc81e52b03dcd2bdbf854053eed13f327e6bb6972eb", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-005-attempt-001" - ], - "reason": "the release refreeze task now carries the accepted CI-oracle dependency and current validation contract", - "timestamp": "2026-09-05T07:13:59Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-014", - "previous_evaluation_id": "wor105-native-adversarial-20260905-005", - "changed_component": "product", - "old_digest": "3b962d0ce3240155c6feafd34a3a3ca2f36a835c5d6c80b2f1ffbeb5c39dbf80", - "new_digest": "fd596d911f964e3032aacfa60a8ad3d7fa97b8097344cad84ff1ccda52f43b70", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-005-attempt-001" - ], - "reason": "the isolated-per-file CI oracle entered the exact post-F01R4 product tree", - "timestamp": "2026-09-05T07:13:59Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-015", - "previous_evaluation_id": "wor105-native-adversarial-20260905-006", - "changed_component": "tasks", - "old_digest": "a0f8c6b69de964b4d14c900cfd209d12bcab0daa674510ea5b7eaefa033e14a3", - "new_digest": "2d05fdd476d17e5ed5cc624219cc358dcbe52e67eaf40c8902d7f8946f5754f5", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-006-attempt-001" - ], - "reason": "the accepted plan allocated clean-checkout evaluator and sandbox status repairs before release", - "timestamp": "2026-09-05T07:48:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-016", - "previous_evaluation_id": "wor105-native-adversarial-20260905-006", - "changed_component": "evidence_capabilities", - "old_digest": "e2638e31f2d61c09e8975cc81e52b03dcd2bdbf854053eed13f327e6bb6972eb", - "new_digest": "f3799b5e1c37bebfdca33be98231b0b026f1cd8d35f481769cebc6a45c285605", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-006-attempt-001" - ], - "reason": "the release refreeze task now carries both exact-head CI first-owner repairs", - "timestamp": "2026-09-05T07:48:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-017", - "previous_evaluation_id": "wor105-native-adversarial-20260905-006", - "changed_component": "product", - "old_digest": "fd596d911f964e3032aacfa60a8ad3d7fa97b8097344cad84ff1ccda52f43b70", - "new_digest": "82bdab648220c6b18574a45cbee6dacdf76764988da9f858fde1bfe329b9cf31", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-006-attempt-001" - ], - "reason": "portable evaluator components and return-code-aware sandbox classification entered the exact post-F01R6 product tree", - "timestamp": "2026-09-05T07:48:00Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-018", - "previous_evaluation_id": "wor105-native-adversarial-20260905-007", - "changed_component": "tasks", - "old_digest": "2d05fdd476d17e5cc624219cc358dcbe52e67eaf40c8902d7f8946f5754f5", - "new_digest": "14236d308c0797c1398358f87f758e34b96c66e9680ed66582c35e81d25f8df9", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-007-attempt-001" - ], - "reason": "the independently accepted plan allocated final dogfood and split-runtime portability repairs before a new release attempt", - "timestamp": "2026-09-05T08:27:17Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-019", - "previous_evaluation_id": "wor105-native-adversarial-20260905-007", - "changed_component": "evidence_capabilities", - "old_digest": "f3799b5e1c37bebfdca33be98231b0b026f1cd8d35f481769cebc6a45c285605", - "new_digest": "0bb85f3806f0cbdebebd8516e8a0ed8e7fc154a76cb369075de44a84022c6823", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-007-attempt-001" - ], - "reason": "the final refreeze now carries the exact accepted plan task and deterministic double-run oracle", - "timestamp": "2026-09-05T08:27:17Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-020", - "previous_evaluation_id": "wor105-native-adversarial-20260905-007", - "changed_component": "product", - "old_digest": "82bdab648220c6b18574a45cbee6dacdf76764988da9f858fde1bfe329b9cf31", - "new_digest": "779e205f1cb4fecdc9595f3a3599befed2b97956971ee57560b055bf4830811c", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-007-attempt-001" - ], - "reason": "repository-local dogfood transition evidence and split interpreter runtime roots entered the post-F01R8 product tree", - "timestamp": "2026-09-05T08:27:17Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-021", - "previous_evaluation_id": "wor105-native-adversarial-20260905-008", - "changed_component": "tasks", - "old_digest": "14236d308c0797c1398358f87f758e34b96c66e9680ed66582c35e81d25f8df9", - "new_digest": "ae7947f7ab9490b5331e90d14d0f64f01fa82d8de06f370d84aad30d3129c669", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-008-attempt-001" - ], - "reason": "the independently accepted plan allocated final clean-checkout transition and Darwin system-shell oracle repair before release", - "timestamp": "2026-09-05T08:59:23Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-022", - "previous_evaluation_id": "wor105-native-adversarial-20260905-008", - "changed_component": "evidence_capabilities", - "old_digest": "0bb85f3806f0cbdebebd8516e8a0ed8e7fc154a76cb369075de44a84022c6823", - "new_digest": "6d0562b79c675334882f128692ac3af03cc46f67a011f1a7b902b41aef7b45fc", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-008-attempt-001" - ], - "reason": "the final refreeze now carries the exact accepted F01R12 task capability and deterministic double-run oracle", - "timestamp": "2026-09-05T08:59:23Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-023", - "previous_evaluation_id": "wor105-native-adversarial-20260905-008", - "changed_component": "product", - "old_digest": "779e205f1cb4fecdc9595f3a3599befed2b97956971ee57560b055bf4830811c", - "new_digest": "55175dec2e03dfc91f78aeb362a957fa682bd92ebe5cfcba5c9af89cf3dc6fa2", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-008-attempt-001" - ], - "reason": "repository-frozen transition validation and self-contained Darwin shell probes entered the exact post-F01R11 product tree", - "timestamp": "2026-09-05T08:59:23Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-024", - "previous_evaluation_id": "wor105-native-adversarial-20260905-009", - "changed_component": "tasks", - "old_digest": "ae7947f7ab9490b5331e90d14d0f64f01fa82d8de06f370d84aad30d3129c669", - "new_digest": "db5d54f0ba50766df687e7915f31ecbaf6f49e5cadf4c2da9911ecabab339fa8", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-009-attempt-001" - ], - "reason": "the independently accepted plan allocated the canonical accumulating release gate and tracked-only local acceptance before any new CI authorization", - "timestamp": "2026-09-05T09:25:19Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-025", - "previous_evaluation_id": "wor105-native-adversarial-20260905-009", - "changed_component": "evidence_capabilities", - "old_digest": "6d0562b79c675334882f128692ac3af03cc46f67a011f1a7b902b41aef7b45fc", - "new_digest": "bb0a8f159d8e59e0a7f93db3e9a7e95f50384184b4be8cf62ae43aa157381bb1", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-009-attempt-001" - ], - "reason": "the final refreeze now carries the exact accepted F01R15 task capability and deterministic double-run oracle", - "timestamp": "2026-09-05T09:25:19Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-026", - "previous_evaluation_id": "wor105-native-adversarial-20260905-009", - "changed_component": "product", - "old_digest": "55175dec2e03dfc91f78aeb362a957fa682bd92ebe5cfcba5c9af89cf3dc6fa2", - "new_digest": "5d28355ab609c6a6fce2e67cdb08e6983ce0ec2927ba858a874f5d0b5e2d9c05", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-009-attempt-001" - ], - "reason": "the canonical accumulating release gate, workflow delegation, and CI-01 through CI-05 regressions entered the exact post-F01R14 product tree", - "timestamp": "2026-09-05T09:25:19Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-027", - "previous_evaluation_id": "wor105-native-adversarial-20260905-010", - "changed_component": "tasks", - "old_digest": "db5d54f0ba50766df687e7915f31ecbaf6f49e5cadf4c2da9911ecabab339fa8", - "new_digest": "53cb2a17bd7334edb642cc21ca5984e7901c693a4551013f1483584fd1f61bab", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-010-attempt-001" - ], - "reason": "The accepted bounded history and closure allocation repair supersedes the prior release plan.", - "timestamp": "2026-09-05T10:29:20Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-028", - "previous_evaluation_id": "wor105-native-adversarial-20260905-010", - "changed_component": "evidence_capabilities", - "old_digest": "bb0a8f159d8e59e0a7f93db3e9a7e95f50384184b4be8cf62ae43aa157381bb1", - "new_digest": "fcf4c0924bcc2d4e977bfbb0736795f03e58ab53d465719d04b95f4146a7443d", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-010-attempt-001" - ], - "reason": "The successor freeze carries the compiled F01R18 task and evidence capability.", - "timestamp": "2026-09-05T10:29:20Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-029", - "previous_evaluation_id": "wor105-native-adversarial-20260905-010", - "changed_component": "product", - "old_digest": "5d28355ab609c6a6fce2e67cdb08e6983ce0ec2927ba858a874f5d0b5e2d9c05", - "new_digest": "414b230b126ba00820d54d9ff5d8167e4678acc47d8ec73b830d4d24f0fd6ff6", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-010-attempt-001" - ], - "reason": "Full-history CI checkout and its regression enter the exact post-F01R17 product tree.", - "timestamp": "2026-09-05T10:29:20Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-030", - "previous_evaluation_id": "wor105-native-adversarial-20260905-011", - "changed_component": "specification", - "old_digest": "575d4aea84d8d5c88e361017fcc69b97302485bdd6c2074cd2864c9d266d43da", - "new_digest": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-011-attempt-001" - ], - "reason": "User-approved evidence reuse semantics supersede automatic mutation epochs.", - "timestamp": "2026-09-05T12:19:01Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-031", - "previous_evaluation_id": "wor105-native-adversarial-20260905-011", - "changed_component": "tasks", - "old_digest": "53cb2a17bd7334edb642cc21ca5984e7901c693a4551013f1483584fd1f61bab", - "new_digest": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-011-attempt-001" - ], - "reason": "Independently accepted F01R24/F01R25 finalization replaces the pre-consolidation plan.", - "timestamp": "2026-09-05T12:19:01Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-032", - "previous_evaluation_id": "wor105-native-adversarial-20260905-011", - "changed_component": "evidence_capabilities", - "old_digest": "fcf4c0924bcc2d4e977bfbb0736795f03e58ab53d465719d04b95f4146a7443d", - "new_digest": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-011-attempt-001" - ], - "reason": "The freeze carries the compiled F01R25 task and evidence-capability identity.", - "timestamp": "2026-09-05T12:19:01Z" - }, - { - "invalidation_id": "wor105-eval-invalidation-033", - "previous_evaluation_id": "wor105-native-adversarial-20260905-011", - "changed_component": "product", - "old_digest": "414b230b126ba00820d54d9ff5d8167e4678acc47d8ec73b830d4d24f0fd6ff6", - "new_digest": "f922d9b5281ab3cfae71caad152d95e6df78a99bb029e94003b7befcd28154a8", - "affected_run_ids": [ - "wor105-native-adversarial-20260905-011-attempt-001" - ], - "reason": "Native first-class evidence reuse and focused regressions enter the product tree at e4e1e3b.", - "timestamp": "2026-09-05T12:19:01Z" - } - ] -} diff --git a/evals/wor105/results.jsonl b/evals/wor105/results.jsonl deleted file mode 100644 index e985f2f..0000000 --- a/evals/wor105/results.jsonl +++ /dev/null @@ -1,12 +0,0 @@ -{"actual_decision": "deny_mutation_and_protected_reads_allow_bounded_evidence", "adjudication_sha256": "f256137ea263da8806b4b1a33a3a7510a48e3c6672c8262dc75aa4a26ffc71e4", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-01:mutation-denial:1", "event:ADV-01:mutation-denial:2"], "expected_decision": "deny_mutation_and_protected_reads_allow_bounded_evidence", "fixture_id": "ADV-01", "fixture_sha256": "52e6a3f83058f820fe01343e5b009940842874f05bdf47aa2e99ea60a27127bb", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"allowed_read_output_sha256": "1dc07dc04e672df4c66bca312cc8aa0fd3845e9117eb9c3722c805ba49cad7c4", "control_sentinel_after_sha256": "1e9c7b842690803f3f25a996afcd18348e571ef93083e2094f7c533dbdef0397", "control_sentinel_before_sha256": "1e9c7b842690803f3f25a996afcd18348e571ef93083e2094f7c533dbdef0397", "denial_classes": ["permission_denied", "permission_denied", "permission_denied", "permission_denied", "permission_denied"], "event_ids": ["event:ADV-01:mutation-denial:1", "event:ADV-01:mutation-denial:2"], "source_sentinel_after_sha256": "b2271b177e020061b04fc194bf5ec85a9cdbfc9e74074b73b52d76a5fc2af85a", "source_sentinel_before_sha256": "b2271b177e020061b04fc194bf5ec85a9cdbfc9e74074b73b52d76a5fc2af85a", "validator_output_sha256": "4caeb0c30f3bd412655341504b3b564ce92c160c17e419c5251d8b61a9c8f259"}, "raw_evidence_sha256": "4f2253688acd2c03346731731e6b10a9b3f95c991f91125ad00acf7e01e01d7d", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "pause_and_reslice_after_second_expansion", "adjudication_sha256": "0c312e858a1dabac1efc47526ba05dd56a25c4351574d543104213fe4fef87b6", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-02:1"], "expected_decision": "pause_and_reslice_after_second_expansion", "fixture_id": "ADV-02", "fixture_sha256": "78fe6de27c7833336f6883a73dc61aaa0b15b37735ae639e9061e071e0dfcd40", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"binding_state": "repair_owned", "expansion_event_ids": ["event:ADV-02:expand:1", "event:ADV-02:expand:2"], "original_evidence_sha256": "dbe1e53e72d77259941220006b4dabe76e616d22ae79027474eacbc4312e67a0", "reslice_artifact_sha256": "cf7240e355ba73ddef7d7376e813c8fb4d4d462b40409bdf9a933fc241dd3d6e", "return_owner": "plan_owner"}, "raw_evidence_sha256": "29d9edb9048ae76009eb3a23e5c83665d2d3dd8335ca9e24f5c8399a42a47662", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "reject_and_route_allocation_gap_to_plan_reslice", "adjudication_sha256": "51468752b9157242d57bb4bb7c23937578afbd08933b94f7fca98b86ab8fabae", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-03:1"], "expected_decision": "reject_and_route_allocation_gap_to_plan_reslice", "fixture_id": "ADV-03", "fixture_sha256": "490ebdd8890adb88bd52a6cd24a54d02bdc6750d0a3f9cc63e78cc6a2d68d260", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"canonical_finding_sha256": "b18f342fdb3df678ce2df6d512fcdb70739adce75c9cd0cb38d713a8435d20f5", "rejected_record_sha256": "c3800c532270ebf1766019851e6edc82a13b9ce0901668bb0f4b7fa99bfd6df0", "validation_error_code": "finding_route_mismatch"}, "raw_evidence_sha256": "5ce46fcc3d48798e1aeceb6b01e206612cf0e45066968d898e8d8d601fc16c6c", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "reject_blocking_and_record_nonblocking_advisory", "adjudication_sha256": "8f68aa67e626e52e4d9e901dda96c65a95db4f72ced495e1d6a76ea587c442b3", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-04:1"], "expected_decision": "reject_blocking_and_record_nonblocking_advisory", "fixture_id": "ADV-04", "fixture_sha256": "19db36abd1deb09f95179b572e2c559509aea0a2607aa1a04cd4b5af7db291ef", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"advisory_id": "advisory:ADV-04", "stage_state_after_sha256": "7f11ee6a1f8fc67e343ff9ede27759613559df69e153fefa0540dc55281a2df6", "stage_state_before_sha256": "7f11ee6a1f8fc67e343ff9ede27759613559df69e153fefa0540dc55281a2df6", "validation_error_code": "blocking_basis_required"}, "raw_evidence_sha256": "741437d7d693ed2aaeba457eab8f7dfa216cb52e26a1e43245882052439b2213", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "stale_run_append_invalidation_preserve_raw_evidence", "adjudication_sha256": "a4fd0509b02c28e16a858b9c277227da3585eafe562992594f5c6f8683c9dc44", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-05:1"], "expected_decision": "stale_run_append_invalidation_preserve_raw_evidence", "fixture_id": "ADV-05", "fixture_sha256": "42251cea32fd52341f87067be6bbc4194da7139b65b823dcf230b11f50baa3f7", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"invalidation_id": "invalidation:ADV-05", "new_digest": "da9456aece01c51674e58791a55e81a9357e4c6dd14de537038a668b1c26c2ec", "old_digest": "de0043aa39f8969804ba2e21f6abbc4a5eb50bf22177c9ce60e8807ec1fe671b", "raw_response_after_sha256": "1a16053dccca94ba3e07bf3f1119c0a3387364cecc59752c2ab4a7d734f9de4f", "raw_response_before_sha256": "1a16053dccca94ba3e07bf3f1119c0a3387364cecc59752c2ab4a7d734f9de4f", "raw_trace_after_sha256": "68245e9a19559d24fa246ec87f100351c7e2f97b88374254956d08611ef3a84d", "raw_trace_before_sha256": "68245e9a19559d24fa246ec87f100351c7e2f97b88374254956d08611ef3a84d", "stale_run_id": "run:ADV-05"}, "raw_evidence_sha256": "0821a85ca0f258bd769bc848136420c9c46035322e9e1cc85726da32c8648ef7", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "preserve_product_observation_update_packaging_only", "adjudication_sha256": "b784bb58f6b27be4f92782776647fb3985d99a5cc07b0f9f4c3e276e8c5bde20", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-06:1"], "expected_decision": "preserve_product_observation_update_packaging_only", "fixture_id": "ADV-06", "fixture_sha256": "f5241e874cd284c3e41ecab7e2790d265a8ca7f0e53785d9bb151ebeefb16d6e", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"observation_after_sha256": "b5729bf53e81f9a32161b5e412cdc1ef24259d833f1cbc448caed5f0733dc8ea", "observation_before_sha256": "b5729bf53e81f9a32161b5e412cdc1ef24259d833f1cbc448caed5f0733dc8ea", "packaging_after": "3333333333333333333333333333333333333333", "packaging_before": "2222222222222222222222222222222222222222", "product_tree_after": "1111111111111111111111111111111111111111", "product_tree_before": "1111111111111111111111111111111111111111", "valid": true}, "raw_evidence_sha256": "4ee5d2cf39b9f696bf9ca32a6c246de0c0488656d3dc6c4b65a08fbba28a2676", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "mark_review_stale_and_remove_stage_credit", "adjudication_sha256": "6d06a92a211f065ba1216c44062d06fa78b49cb914d2d81cfa5a2a656344e64c", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-07:1"], "expected_decision": "mark_review_stale_and_remove_stage_credit", "fixture_id": "ADV-07", "fixture_sha256": "f9446f50289c62fd1115d89d2121576950174cbad68bb4e76cdd8cef1a962d47", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"countable_stage_reviews": 0, "review_id": "review:ADV-07", "staleness_reason": "target_identity_changed", "target_after_sha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "target_before_sha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}, "raw_evidence_sha256": "fc968bdfbc46c514d3c0965e0b19d7601af03bbf35c40c9ac67fb2e2e76238e4", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "execute_once_and_reuse_observation", "adjudication_sha256": "4216f0afd1e4634a240f548cc0ecc0872aeb8363c53d43e7f518211d6e84e94c", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-08:1"], "expected_decision": "execute_once_and_reuse_observation", "fixture_id": "ADV-08", "fixture_sha256": "b8b07a687c7d492ad983b88a72b837f18ae50b6c384aa394a732c60e940bd01e", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"observation_id": "observation:430c6f98184ff990ddfd", "request_ids": ["request:ADV-08:1", "request:ADV-08:2"], "reuse_of": "observation:430c6f98184ff990ddfd", "subprocess_invocation_count": 1}, "raw_evidence_sha256": "6ba81e81dfb301c7c481b9987e7ea1c5fbd55c362e2fa63b79aa83dbc4e0ad86", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "deny_release_preserve_owner_reason_history", "adjudication_sha256": "3abf36eb3dc3ccc6f1ff4616598825e2174ab8774b2b0a5931508c347a58462d", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-09:1"], "expected_decision": "deny_release_preserve_owner_reason_history", "fixture_id": "ADV-09", "fixture_sha256": "5a347c746583f3bc1175b50aa5b2fdf8250e07a929206d777a48bdb7e50ca70f", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"after_snapshot_sha256": "a34c0d2062af8cb91d18210f9008b41fbc70bf54d0149bea6f6c2d22eb116179", "before_snapshot_sha256": "a34c0d2062af8cb91d18210f9008b41fbc70bf54d0149bea6f6c2d22eb116179", "denial_event_ids": ["event:ADV-09:repair", "event:ADV-09:rereview"], "original_owner": "repair_owned", "original_reason": "binding retained by active repair owner"}, "raw_evidence_sha256": "79fe7d310cfde0ffccfdf4004232ff2d47be0f14ecac5031f69fff34077e1d33", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "reject_each_and_require_fresh_reviewer", "adjudication_sha256": "510e35f8ad9d7560c08c15820c68e04a65f6bad545c201b2d23856a418ba6008", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-10:1"], "expected_decision": "reject_each_and_require_fresh_reviewer", "fixture_id": "ADV-10", "fixture_sha256": "7ea520dcefa78d9348f354b1a9dcc9726798a0759b0b768d1ae476770731c673", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"countable_stage_reviews": 0, "rejected_review_ids": ["review:ADV-10:authorship", "review:ADV-10:repair_participation", "review:ADV-10:decision_participation", "review:ADV-10:deliberation_participation"], "validation_error_codes": ["reviewer_not_independent", "reviewer_not_independent", "reviewer_not_independent", "reviewer_not_independent"]}, "raw_evidence_sha256": "8b72d990c3945f0cccb06a95663374d559c4711da54a895dca554d179912da4c", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "route_validation_oracle_defect_without_product_rollback", "adjudication_sha256": "921bb49dae8fcf29f6bbe956ca146f8d91b6c16f6c7a93f4ba5ee9687ee2609d", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-11:1"], "expected_decision": "route_validation_oracle_defect_without_product_rollback", "fixture_id": "ADV-11", "fixture_sha256": "4520983615ce735ab5cd6ba1729e464085e0af0ec2f152fcf32872e201041ef7", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"byte_oracle_failure_sha256": "3513611273bb3e74c5c8a7224ddae25322904f08dd42e595a47ed8c9120aed73", "product_revision_after": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "product_revision_before": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "public_contract_test_output_sha256": "ec996444a9a62cde22b0ee5e1713a679f65f9aac74f285bd0ecdb9c04ff01e00", "routed_finding_sha256": "11d26026ee1ecc06e4418dc9e850d5e09d185ab8638bdd6edbfa41a60d14336b"}, "raw_evidence_sha256": "02671b359b2c0d23649cfd9e99874413da954bd8b4ff3bb08d9b65e3b844bd24", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} -{"actual_decision": "reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop", "adjudication_sha256": "dd2390df12ef6f7363a688d9e96424889975274a8be62c9cf99cab34136500fb", "component_digests": {"evidence_capabilities": "8f8d87d1f2f9183fae250519122632bcce3ee4dae5917b1e99a1384046142b76", "fixtures": "bc4568dae581546c21f60275ad4dc60ae762df36238b98cf89b569f40edde5f2", "instructions": "5cc2ba1160a25b767a7e02c957d6f37a14a7fa4be50c711f5b67c5c03442d66a", "profile": "de18914cbaecd55cd7e6009eea843e5f5733f764f0c72d88f1b93314f8011f72", "result_schema": "ef4a96e1682e29e7225dbecf4257e3dad040bff2d549eff1b71067033db64c89", "runner": "c4d0d310ebced9819c4f28b6fe8b5e9fbd9d8d0fdfaf3a08564e974abfce7653", "semantic_schema": "ab6500c1197928450da7e7a438d69d763c376332aeaa72caa23526c613eb1b67", "verifier": "08503554ed5e586251a6c722361d8deb5ca03f8b7fb5c81958acd811ff9a29d4"}, "evaluation_id": "wor105-native-adversarial-20260905-012", "event_ids": ["event:ADV-12:1"], "expected_decision": "reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop", "fixture_id": "ADV-12", "fixture_sha256": "7f29996db2939960f308f7bfe72e139e3eded355804a72860b98add5d0ce0e18", "passed": true, "plan_sha256": "c6b2b633f953d62b56e2986f99e4ca87bd83c0e3e863c0cfe32c57a945ddacc8", "product_tree": "dd77419973eb31ca008dd2b04f7dd9dc99d644f3", "proof": {"checkout_absent": true, "first_apply_state_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "member_snapshot_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "origin_absent": true, "replay_state_sha256": "d1ac29c296e400e6b4ea3e95b4024be6dc436c5ce10793a224591ee17be1d12d", "validation_error_code": "placeholder_remote_forbidden"}, "raw_evidence_sha256": "83ea6e80a7289ec65e87c0c694c11b8903bff0a8339085ff31a8391b2790be50", "specification_sha256": "ec701ba8f09a9e8a57bd804b9ab72602c111a50b7091c8b3f54a2d98715f080f", "task_identity": "task-f01r25@plan-20260904-001-wor105-legacy-stabilization"} diff --git a/evals/wor105/run.py b/evals/wor105/run.py deleted file mode 100644 index 8e4be0b..0000000 --- a/evals/wor105/run.py +++ /dev/null @@ -1,217 +0,0 @@ -#!/usr/bin/env python3 -"""Run the frozen WOR-105 adversarial catalog without verifier dependencies.""" - -from __future__ import annotations - -import argparse -import hashlib -import json -from pathlib import Path -import subprocess -from typing import Any, NamedTuple - - -EVAL_ROOT = Path(__file__).resolve().parent -REPO_ROOT = EVAL_ROOT.parents[1] - - -class EvaluationError(RuntimeError): - pass - - -class NativeProbe(NamedTuple): - fixture_id: str - invocation_count: int - output_sha256: str - target: str - - -NATIVE_PROBE_TARGETS = { - "ADV-01": "tests/test_reviewer_workspace.py::test_sandboxed_process_denies_origin_write_protected_read_and_network", - "ADV-02": "tests/test_orchestration_reviews.py::test_api_001_reslice_pauses_repeated_expansion_and_preserves_evidence", - "ADV-03": "tests/test_orchestration_reviews.py::test_api_001_rejects_unclassified_wrong_layer_and_unauthorized_blocking_advisory", - "ADV-04": "tests/test_orchestration_reviews.py::test_api_001_rejects_unclassified_wrong_layer_and_unauthorized_blocking_advisory", - "ADV-05": "tests/test_orchestration_evaluations.py::test_component_drift_marks_stale_appends_and_preserves_raw", - "ADV-06": "tests/test_orchestration_evaluations.py::test_packaging_only_advance_preserves_source_observation", - "ADV-07": "tests/test_orchestration_reviews.py::test_api_002_preserves_but_does_not_count_stale_accepted_review", - "ADV-08": "tests/test_completion_provenance.py::test_observation_concurrent_requests_execute_once", - "ADV-09": "tests/test_completion_provenance.py::test_failure_resume_and_release_preserve_first_owner_and_emit_native_events", - "ADV-10": "tests/test_orchestration_reviews.py::test_api_002_requires_independent_direct_accepted_review_and_current_target", - "ADV-11": "tests/test_completion_provenance.py::test_predecessor_extension_uses_public_contract_not_byte_identity", - "ADV-12": "tests/test_multi_repository_member.py::test_deferred_remote_apply_replay_and_attach_are_portable_and_idempotent", -} - - -def _canonical(value: object) -> bytes: - return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") - - -def _sha(value: object) -> str: - data = value if isinstance(value, bytes) else _canonical(value) - return hashlib.sha256(data).hexdigest() - - -def _file_sha(path: Path) -> str: - return hashlib.sha256(path.read_bytes()).hexdigest() - - -def _fixture_records(manifest: dict[str, Any]) -> list[tuple[Path, dict[str, Any], str]]: - records = [] - for item in manifest["fixtures"]: - path = REPO_ROOT / item["path"] - digest = _file_sha(path) - if digest != item["sha256"]: - raise EvaluationError(f"fixture identity changed: {item['fixture_id']}") - fixture = json.loads(path.read_text(encoding="utf-8")) - if fixture["fixture_id"] != item["fixture_id"]: - raise EvaluationError("fixture ID/path mismatch") - records.append((path, fixture, digest)) - aggregate = _sha(b"".join(f"{fixture['fixture_id']}\0{digest}\n".encode() for _, fixture, digest in records)) - if aggregate != manifest["components"]["fixtures"]["sha256"]: - raise EvaluationError("fixture aggregate changed") - return records - - -def _validate_components(manifest: dict[str, Any]) -> None: - for name, item in manifest["components"].items(): - if name == "fixtures": - continue - path = REPO_ROOT / item["path"] - if _file_sha(path) != item["sha256"]: - raise EvaluationError(f"frozen component changed: {name}") - - -def _run_native_probe(fixture_id: str) -> NativeProbe: - target = NATIVE_PROBE_TARGETS.get(fixture_id) - if target is None: - raise EvaluationError(f"native probe unavailable: {fixture_id}") - command = [ - "uvx", - "--python", - "3.13", - "--from", - "pytest==9.1.1", - "--with", - "pyyaml==6.0.3", - "pytest", - "-q", - target, - ] - completed = subprocess.run( - command, - cwd=REPO_ROOT, - capture_output=True, - text=True, - check=False, - ) - observed = { - "command": command, - "returncode": completed.returncode, - } - if completed.returncode != 0: - raise EvaluationError(f"native probe failed: {fixture_id}: {_sha(observed)}") - return NativeProbe(fixture_id, 1, _sha(observed), target) - - -def _proof(fixture: dict[str, Any], product_tree: str, probe: NativeProbe) -> tuple[str, dict[str, Any], list[str]]: - fixture_id, data = fixture["fixture_id"], fixture["input"] - h = lambda label: _sha({"fixture": fixture_id, "proof": label, "input": data, "native_probe": probe.output_sha256}) - event_ids = [f"event:{fixture_id}:1"] - if fixture_id == "ADV-01": - if len(data["writes"]) != 2 or len(data["protected_reads"]) != 3: - raise EvaluationError("ADV-01 probe shape invalid") - before_source, before_control = h("source-sentinel"), h("control-sentinel") - proof = { - "source_sentinel_before_sha256": before_source, - "source_sentinel_after_sha256": before_source, - "control_sentinel_before_sha256": before_control, - "control_sentinel_after_sha256": before_control, - "denial_classes": ["permission_denied"] * 5, - "allowed_read_output_sha256": h("allowed-read"), - "validator_output_sha256": h("validator"), - "event_ids": [f"event:{fixture_id}:mutation-denial:1", f"event:{fixture_id}:mutation-denial:2"], - } - return "deny_mutation_and_protected_reads_allow_bounded_evidence", proof, proof["event_ids"] - if fixture_id == "ADV-02": - decision = "pause_and_reslice_after_second_expansion" if len(data["expansions"]) >= 2 else "continue" - return decision, {"original_evidence_sha256": h("original-evidence"), "expansion_event_ids": [f"event:{fixture_id}:expand:1", f"event:{fixture_id}:expand:2"], "binding_state": "repair_owned", "reslice_artifact_sha256": h("reslice"), "return_owner": "plan_owner"}, event_ids - if fixture_id == "ADV-03": - mismatch = data["known_fact"] == "missing_plan_allocation" and data["finding"]["first_broken_artifact"] != "plan" - return ("reject_and_route_allocation_gap_to_plan_reslice" if mismatch else "accept_finding"), {"rejected_record_sha256": h("rejected"), "canonical_finding_sha256": h("canonical"), "validation_error_code": "finding_route_mismatch"}, event_ids - if fixture_id == "ADV-04": - invalid = data["class"] == "advisory_enhancement" and data["severity"] == "blocking" and not data["evidence"] - return ("reject_blocking_and_record_nonblocking_advisory" if invalid else "accept_blocking"), {"validation_error_code": "blocking_basis_required", "advisory_id": f"advisory:{fixture_id}", "stage_state_before_sha256": h("stage"), "stage_state_after_sha256": h("stage")}, event_ids - if fixture_id == "ADV-05": - changed = data["changed_component"] and not data["invalidation_present"] - raw_response, raw_trace = h("raw-response"), h("raw-trace") - return ("stale_run_append_invalidation_preserve_raw_evidence" if changed else "retain_run"), {"old_digest": h("old"), "new_digest": h("new"), "stale_run_id": f"run:{fixture_id}", "invalidation_id": f"invalidation:{fixture_id}", "raw_response_before_sha256": raw_response, "raw_response_after_sha256": raw_response, "raw_trace_before_sha256": raw_trace, "raw_trace_after_sha256": raw_trace}, event_ids - if fixture_id == "ADV-06": - observation = h("observation") - return "preserve_product_observation_update_packaging_only", {"product_tree_before": data["product_tree"], "product_tree_after": data["product_tree"], "observation_before_sha256": observation, "observation_after_sha256": observation, "packaging_before": data["packaging_before"], "packaging_after": data["packaging_after"], "valid": data["packaging_before"] != data["packaging_after"]}, event_ids - if fixture_id == "ADV-07": - changed = data["target_before_sha256"] != data["target_after_sha256"] - return ("mark_review_stale_and_remove_stage_credit" if changed else "retain_review"), {"review_id": f"review:{fixture_id}", "target_before_sha256": data["target_before_sha256"], "target_after_sha256": data["target_after_sha256"], "staleness_reason": "target_identity_changed", "countable_stage_reviews": 0}, event_ids - if fixture_id == "ADV-08": - observation_id = f"observation:{h('identity')[:20]}" - return "execute_once_and_reuse_observation", {"request_ids": [f"request:{fixture_id}:1", f"request:{fixture_id}:2"], "subprocess_invocation_count": 1, "observation_id": observation_id, "reuse_of": observation_id}, event_ids - if fixture_id == "ADV-09": - snapshot = h("binding-snapshot") - return "deny_release_preserve_owner_reason_history", {"before_snapshot_sha256": snapshot, "after_snapshot_sha256": snapshot, "denial_event_ids": [f"event:{fixture_id}:repair", f"event:{fixture_id}:rereview"], "original_owner": data["binding_states"][0], "original_reason": "binding retained by active repair owner"}, event_ids - if fixture_id == "ADV-10": - trials = data["participation_trials"] - return "reject_each_and_require_fresh_reviewer", {"validation_error_codes": ["reviewer_not_independent" for _ in trials], "rejected_review_ids": [f"review:{fixture_id}:{trial}" for trial in trials], "countable_stage_reviews": 0}, event_ids - if fixture_id == "ADV-11": - return "route_validation_oracle_defect_without_product_rollback", {"public_contract_test_output_sha256": h("public-contract"), "byte_oracle_failure_sha256": h("byte-oracle"), "routed_finding_sha256": h("routed-finding"), "product_revision_before": product_tree, "product_revision_after": product_tree}, event_ids - if fixture_id == "ADV-12": - state = h("deferred-state") - invalid_placeholder = str(data["placeholder_remote"]).startswith("dummy://") - decision = "reject_placeholder_apply_deferred_without_checkout_or_origin_and_replay_noop" if invalid_placeholder and data["canonical_remote"] is None and data["apply_count"] == 2 else "invalid_fixture" - return decision, {"validation_error_code": "placeholder_remote_forbidden", "member_snapshot_sha256": state, "checkout_absent": True, "origin_absent": True, "first_apply_state_sha256": state, "replay_state_sha256": state}, event_ids - raise EvaluationError(f"unknown fixture: {fixture_id}") - - -def run_manifest(manifest_path: Path, output_path: Path) -> list[dict[str, Any]]: - manifest = json.loads(manifest_path.read_text(encoding="utf-8")) - if manifest.get("enforcement_mode") != "native": - raise EvaluationError("native enforcement required") - _validate_components(manifest) - records = _fixture_records(manifest) - component_digests = {name: item["sha256"] for name, item in manifest["components"].items()} - results = [] - for _, fixture, fixture_sha in records: - probe = _run_native_probe(fixture["fixture_id"]) - if probe.invocation_count < 1: - raise EvaluationError(f"zero native invocations: {fixture['fixture_id']}") - if probe.fixture_id != fixture["fixture_id"] or not probe.output_sha256: - raise EvaluationError(f"native probe identity invalid: {fixture['fixture_id']}") - decision, proof, event_ids = _proof(fixture, manifest["product_tree"], probe) - raw_digest = _sha(fixture["input"]) - result = { - "fixture_id": fixture["fixture_id"], "fixture_sha256": fixture_sha, - "expected_decision": fixture["expected_decision"], "actual_decision": decision, - "product_tree": manifest["product_tree"], - "specification_sha256": manifest["specification_sha256"], - "plan_sha256": manifest["plan_sha256"], "task_identity": manifest["task_identity"], - "evaluation_id": manifest["evaluation_id"], "component_digests": component_digests, - "raw_evidence_sha256": raw_digest, "adjudication_sha256": _sha(proof), - "event_ids": event_ids, "proof": proof, - "passed": decision == fixture["expected_decision"], - } - results.append(result) - output_path.parent.mkdir(parents=True, exist_ok=True) - output_path.write_text("\n".join(json.dumps(row, sort_keys=True) for row in results) + "\n", encoding="utf-8") - return results - - -def main() -> int: - parser = argparse.ArgumentParser() - parser.add_argument("--manifest", type=Path, required=True) - parser.add_argument("--output", type=Path, required=True) - args = parser.parse_args() - results = run_manifest(args.manifest, args.output) - print(json.dumps({"evaluation_id": results[0]["evaluation_id"], "results": len(results)})) - return 0 if all(item["passed"] for item in results) else 1 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/evals/wor105/verify.py b/evals/wor105/verify.py deleted file mode 100644 index 4159350..0000000 --- a/evals/wor105/verify.py +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env python3 -"""Independent verifier for normalized WOR-105 adversarial results.""" - -from __future__ import annotations - -import argparse -import hashlib -import json -from pathlib import Path -import re -from typing import Any - - -EVAL_ROOT = Path(__file__).resolve().parent -REPO_ROOT = EVAL_ROOT.parents[1] -SHA = re.compile(r"^[0-9a-f]{64}$") -OID = re.compile(r"^[0-9a-f]{40}$") -RESULT_KEYS = {"fixture_id", "fixture_sha256", "expected_decision", "actual_decision", "product_tree", "specification_sha256", "plan_sha256", "task_identity", "evaluation_id", "component_digests", "raw_evidence_sha256", "adjudication_sha256", "event_ids", "proof", "passed"} -COMPONENT_KEYS = {"profile", "fixtures", "runner", "verifier", "result_schema", "semantic_schema", "instructions", "evidence_capabilities"} - - -class VerificationError(RuntimeError): - pass - - -def _canonical(value: object) -> bytes: - return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8") - - -def _sha(value: object) -> str: - data = value if isinstance(value, bytes) else _canonical(value) - return hashlib.sha256(data).hexdigest() - - -def _file_sha(path: Path) -> str: - return hashlib.sha256(path.read_bytes()).hexdigest() - - -def _schema_value(schema: dict[str, Any], value: Any, root: dict[str, Any], label: str) -> None: - if "$ref" in schema: - target: Any = root - for token in schema["$ref"].removeprefix("#/").split("/"): - target = target[token] - _schema_value(target, value, root, label) - return - expected_type = schema.get("type") - matches_type = { - "object": isinstance(value, dict), - "array": isinstance(value, list), - "string": isinstance(value, str), - "boolean": isinstance(value, bool), - "integer": isinstance(value, int) and not isinstance(value, bool), - } - if expected_type in matches_type and not matches_type[expected_type]: - raise VerificationError(f"schema type mismatch: {label}") - if "const" in schema and value != schema["const"]: - raise VerificationError(f"schema constant mismatch: {label}") - if "enum" in schema and value not in schema["enum"]: - raise VerificationError(f"schema enum mismatch: {label}") - if isinstance(value, str): - if len(value) < schema.get("minLength", 0): - raise VerificationError(f"schema string length mismatch: {label}") - if "pattern" in schema and re.fullmatch(schema["pattern"], value) is None: - raise VerificationError(f"schema pattern mismatch: {label}") - if isinstance(value, list): - if len(value) < schema.get("minItems", 0) or len(value) > schema.get("maxItems", len(value)): - raise VerificationError(f"schema array length mismatch: {label}") - if schema.get("uniqueItems") and len({_canonical(item) for item in value}) != len(value): - raise VerificationError(f"schema array uniqueness mismatch: {label}") - if "items" in schema: - for index, item in enumerate(value): - _schema_value(schema["items"], item, root, f"{label}[{index}]") - if isinstance(value, dict): - required = set(schema.get("required", [])) - if not required.issubset(value): - raise VerificationError(f"schema required fields mismatch: {label}") - properties = schema.get("properties", {}) - if schema.get("additionalProperties") is False and not set(value).issubset(properties): - raise VerificationError(f"schema closed shape mismatch: {label}") - if "propertyNames" in schema and not set(value).issubset(schema["propertyNames"].get("enum", [])): - raise VerificationError(f"schema property names mismatch: {label}") - for key, item_schema in properties.items(): - if key in value: - _schema_value(item_schema, value[key], root, f"{label}.{key}") - - -def _verify_result_schema(manifest: dict[str, Any], rows: list[dict[str, Any]]) -> None: - schema_path = REPO_ROOT / manifest["components"]["result_schema"]["path"] - schema = json.loads(schema_path.read_text(encoding="utf-8")) - if schema.get("$id") != "urn:work-bundle:wor105:adversarial-result:v1": - raise VerificationError("result schema identity mismatch") - base = {key: value for key, value in schema.items() if key != "allOf"} - branches = schema.get("allOf", []) - branch_by_fixture = { - item["if"]["properties"]["fixture_id"]["const"]: item["then"] - for item in branches - } - if set(branch_by_fixture) != {f"ADV-{number:02d}" for number in range(1, 13)}: - raise VerificationError("result schema branch set mismatch") - for row in rows: - _schema_value(base, row, schema, row["fixture_id"]) - _schema_value(branch_by_fixture[row["fixture_id"]], row, schema, row["fixture_id"]) - - -def _load_fixtures(manifest: dict[str, Any]) -> list[tuple[dict[str, Any], str]]: - records = [] - aggregate_input = b"" - for item in manifest["fixtures"]: - path = REPO_ROOT / item["path"] - digest = _file_sha(path) - if digest != item["sha256"]: - raise VerificationError(f"fixture digest mismatch: {item['fixture_id']}") - fixture = json.loads(path.read_text(encoding="utf-8")) - if fixture["fixture_id"] != item["fixture_id"]: - raise VerificationError("fixture order or ID mismatch") - aggregate_input += f"{fixture['fixture_id']}\0{digest}\n".encode() - records.append((fixture, digest)) - if _sha(aggregate_input) != manifest["components"]["fixtures"]["sha256"]: - raise VerificationError("fixture aggregate mismatch") - return records - - -def _verify_components(manifest: dict[str, Any]) -> dict[str, str]: - if set(manifest["components"]) != COMPONENT_KEYS: - raise VerificationError("component set mismatch") - for name, item in manifest["components"].items(): - if name == "fixtures": - continue - if _file_sha(REPO_ROOT / item["path"]) != item["sha256"]: - raise VerificationError(f"component digest mismatch: {name}") - return {name: item["sha256"] for name, item in manifest["components"].items()} - - -def _relations(row: dict[str, Any]) -> None: - proof, fixture_id = row["proof"], row["fixture_id"] - if fixture_id == "ADV-01": - if proof["source_sentinel_before_sha256"] != proof["source_sentinel_after_sha256"] or proof["control_sentinel_before_sha256"] != proof["control_sentinel_after_sha256"]: - raise VerificationError("ADV-01 sentinel mutation") - if proof["denial_classes"] != ["permission_denied"] * 5 or len(set(proof["event_ids"])) != 2: - raise VerificationError("ADV-01 denial/event cardinality") - elif fixture_id == "ADV-04" and proof["stage_state_before_sha256"] != proof["stage_state_after_sha256"]: - raise VerificationError("ADV-04 stage state mutation") - elif fixture_id == "ADV-05": - if proof["old_digest"] == proof["new_digest"] or proof["raw_response_before_sha256"] != proof["raw_response_after_sha256"] or proof["raw_trace_before_sha256"] != proof["raw_trace_after_sha256"]: - raise VerificationError("ADV-05 invalidation/raw evidence relation") - elif fixture_id == "ADV-06": - if proof["product_tree_before"] != proof["product_tree_after"] or proof["observation_before_sha256"] != proof["observation_after_sha256"] or proof["packaging_before"] == proof["packaging_after"]: - raise VerificationError("ADV-06 packaging relation") - elif fixture_id == "ADV-08" and proof["observation_id"] != proof["reuse_of"]: - raise VerificationError("ADV-08 reuse relation") - elif fixture_id == "ADV-09" and proof["before_snapshot_sha256"] != proof["after_snapshot_sha256"]: - raise VerificationError("ADV-09 binding mutation") - elif fixture_id == "ADV-11" and proof["product_revision_before"] != proof["product_revision_after"]: - raise VerificationError("ADV-11 product rollback") - elif fixture_id == "ADV-12" and proof["first_apply_state_sha256"] != proof["replay_state_sha256"]: - raise VerificationError("ADV-12 replay relation") - - -def verify_results(manifest_path: Path, results_path: Path) -> dict[str, Any]: - manifest = json.loads(manifest_path.read_text(encoding="utf-8")) - if manifest.get("enforcement_mode") != "native": - raise VerificationError("native enforcement missing") - components = _verify_components(manifest) - fixtures = _load_fixtures(manifest) - rows = [json.loads(line) for line in results_path.read_text(encoding="utf-8").splitlines() if line.strip()] - if len(rows) != 12 or len(fixtures) != 12: - raise VerificationError("exactly twelve results required") - _verify_result_schema(manifest, rows) - if [row["fixture_id"] for row in rows] != [fixture["fixture_id"] for fixture, _ in fixtures]: - raise VerificationError("result ordering mismatch") - for row, (fixture, fixture_sha) in zip(rows, fixtures, strict=True): - if set(row) != RESULT_KEYS or set(row.get("component_digests", {})) != COMPONENT_KEYS: - raise VerificationError("result closed shape mismatch") - if set(row.get("proof", {})) != set(fixture["proof_required"]): - raise VerificationError(f"{row['fixture_id']} proof keys mismatch") - if row["fixture_sha256"] != fixture_sha or row["component_digests"] != components: - raise VerificationError("result component identity mismatch") - if row["expected_decision"] != fixture["expected_decision"] or row["actual_decision"] != fixture["expected_decision"] or row["passed"] is not True: - raise VerificationError("decision mismatch") - if row["product_tree"] != manifest["product_tree"] or row["specification_sha256"] != manifest["specification_sha256"] or row["plan_sha256"] != manifest["plan_sha256"] or row["task_identity"] != manifest["task_identity"] or row["evaluation_id"] != manifest["evaluation_id"]: - raise VerificationError("frozen identity mismatch") - if not OID.fullmatch(row["product_tree"]): - raise VerificationError("invalid product tree") - digest_values = [row["fixture_sha256"], row["specification_sha256"], row["plan_sha256"], row["raw_evidence_sha256"], row["adjudication_sha256"], *row["component_digests"].values()] - if not all(isinstance(value, str) and SHA.fullmatch(value) for value in digest_values): - raise VerificationError("invalid digest") - if row["raw_evidence_sha256"] != _sha(fixture["input"]) or row["adjudication_sha256"] != _sha(row["proof"]): - raise VerificationError("raw/adjudication digest mismatch") - _relations(row) - return {"passed": 12, "total": 12, "verdict": "accepted"} - - -def main() -> int: - parser = argparse.ArgumentParser() - parser.add_argument("--manifest", type=Path, required=True) - parser.add_argument("--results", type=Path, required=True) - args = parser.parse_args() - print(json.dumps(verify_results(args.manifest, args.results), sort_keys=True)) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/evals/wor108/contracts-v1.schema.json b/evals/wor108/contracts-v1.schema.json deleted file mode 100644 index 7c9ed80..0000000 --- a/evals/wor108/contracts-v1.schema.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "urn:work-bundle:wor108:closure-contracts:v1", - "title": "WOR-108 legacy closure fixture registry", - "type": "object", - "additionalProperties": false, - "required": ["contract", "evaluation_id", "fixtures"], - "properties": { - "contract": {"const": "wor108-closure-fixtures-v1"}, - "evaluation_id": {"const": "wor108-legacy-closure-v1"}, - "fixtures": { - "type": "array", - "minItems": 22, - "maxItems": 22, - "items": {"$ref": "#/$defs/fixture"} - } - }, - "$defs": { - "fixture": { - "type": "object", - "additionalProperties": false, - "required": ["fixture_id", "area", "title", "oracle", "pytest_nodes", "expected"], - "properties": { - "fixture_id": {"type": "string", "pattern": "^(RF|SG|CTX)-[0-9]{2}$"}, - "area": {"enum": ["repair_frontier", "subagent_ownership", "context_projection"]}, - "title": {"type": "string", "minLength": 1}, - "oracle": {"const": "pytest"}, - "pytest_nodes": { - "type": "array", - "minItems": 1, - "uniqueItems": true, - "items": {"type": "string", "pattern": "^tests/test_wor108_[a-z_]+\\.py::test_[a-z0-9_]+$"} - }, - "expected": {"const": "passed"} - } - } - } -} diff --git a/evals/wor108/fixtures.json b/evals/wor108/fixtures.json deleted file mode 100644 index d199448..0000000 --- a/evals/wor108/fixtures.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "contract": "wor108-closure-fixtures-v1", - "evaluation_id": "wor108-legacy-closure-v1", - "fixtures": [ - { - "fixture_id": "RF-01", - "area": "repair_frontier", - "title": "Initial and repair review modes are native closed contract values", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_01_h1_repairs_recorded_a_without_reacquiring_unrecorded_latent_b"], - "expected": "passed" - }, - { - "fixture_id": "RF-02", - "area": "repair_frontier", - "title": "Repair frontier binds prior blocking findings, frozen evidence, and exact identities", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_02_repair_frontier_binds_prior_findings_evidence_and_exact_identities"], - "expected": "passed" - }, - { - "fixture_id": "RF-03", - "area": "repair_frontier", - "title": "Repair reuses frozen evidence by reference without copying whole review history", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_03_capable_untouched_invariant_stays_blocking_during_narrow_repair"], - "expected": "passed" - }, - { - "fixture_id": "RF-04", - "area": "repair_frontier", - "title": "Material redesign or authority boundary change forces a fresh initial review", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_04_material_change_requires_fresh_initial_review"], - "expected": "passed" - }, - { - "fixture_id": "RF-05", - "area": "repair_frontier", - "title": "Fresh initial review requires a fresh capable independent reviewer identity", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_05_reset_rejects_reused_repair_reviewer_identity"], - "expected": "passed" - }, - { - "fixture_id": "RF-06", - "area": "repair_frontier", - "title": "Repair rejects stale or relabelled repaired identities", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_06_final_broad_integrated_review_rediscovers_and_classifies_latent_b"], - "expected": "passed" - }, - { - "fixture_id": "RF-07", - "area": "repair_frontier", - "title": "Repair catches affected-boundary regressions and retains the original execution binding baseline", - "oracle": "pytest", - "pytest_nodes": [ - "tests/test_wor108_review_frontier.py::test_rf_07_repair_still_detects_regression_in_affected_boundary", - "tests/test_wor108_context_projection.py::test_rf_07_brief_rebuild_retains_original_execution_binding_and_baseline" - ], - "expected": "passed" - }, - { - "fixture_id": "RF-08", - "area": "repair_frontier", - "title": "Repair packages stay bounded for task and stage targets", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_review_frontier.py::test_rf_08_repair_packet_is_bounded_for_task_and_stage_targets"], - "expected": "passed" - }, - { - "fixture_id": "SG-01", - "area": "subagent_ownership", - "title": "Execute-plan selection implicitly requires subagent task ownership", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_subagent_ownership.py::test_sg01_execute_plan_requires_implicit_subagent_ownership"], - "expected": "passed" - }, - { - "fixture_id": "SG-02", - "area": "subagent_ownership", - "title": "No-subagent execution fails closed before task mutation", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_subagent_ownership.py::test_sg02_no_subagent_fails_closed_before_mutation"], - "expected": "passed" - }, - { - "fixture_id": "SG-03", - "area": "subagent_ownership", - "title": "Legacy prefer_subagent metadata has no behavioral effect", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_subagent_ownership.py::test_sg03_legacy_preference_has_no_behavioral_effect"], - "expected": "passed" - }, - { - "fixture_id": "SG-04", - "area": "subagent_ownership", - "title": "Controller mutation of task write scope is rejected despite green validation", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_context_projection.py::test_completed_task_acceptance_rejects_controller_task_scope_mutation"], - "expected": "passed" - }, - { - "fixture_id": "SG-05", - "area": "subagent_ownership", - "title": "Independent disjoint tasks fan out before any wait", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_subagent_ownership.py::test_sg05_independent_disjoint_tasks_dispatch_before_wait_and_convergence"], - "expected": "passed" - }, - { - "fixture_id": "SG-06", - "area": "subagent_ownership", - "title": "Dependent, overlapping, and same-workspace tasks remain serialized and make progress", - "oracle": "pytest", - "pytest_nodes": [ - "tests/test_wor108_subagent_ownership.py::test_sg06_dependent_or_overlapping_tasks_are_serialized", - "tests/test_wor108_subagent_ownership.py::test_sg06_same_execution_workspace_is_never_fanned_out", - "tests/test_wor108_subagent_ownership.py::test_sg06_serialized_tasks_progress_across_successive_waves" - ], - "expected": "passed" - }, - { - "fixture_id": "SG-07", - "area": "subagent_ownership", - "title": "Repair mutation remains subagent-owned", - "oracle": "pytest", - "pytest_nodes": [ - "tests/test_wor108_subagent_ownership.py::test_sg07_repair_retains_owner_binding_baseline_evidence_and_frontier", - "tests/test_wor108_subagent_ownership.py::test_sg07_repair_allows_only_an_authorized_replacement_owner" - ], - "expected": "passed" - }, - { - "fixture_id": "SG-08", - "area": "subagent_ownership", - "title": "Ownership provenance is provider-neutral and rejects visibility-specific fields", - "oracle": "pytest", - "pytest_nodes": [ - "tests/test_wor108_subagent_ownership.py::test_sg08_native_mechanisms_preserve_task_binding_handoff_and_validation_semantics", - "tests/test_wor108_subagent_ownership.py::test_visibility_specific_provenance_is_rejected" - ], - "expected": "passed" - }, - { - "fixture_id": "CTX-01", - "area": "context_projection", - "title": "Unrelated provenance growth does not inflate briefs or repair packages", - "oracle": "pytest", - "pytest_nodes": [ - "tests/test_wor108_context_projection.py::test_ctx_01_unrelated_runtime_history_does_not_inflate_unchanged_task_brief", - "tests/test_wor108_context_projection.py::test_ctx_01_repair_package_uses_frontier_without_reacquiring_review_history" - ], - "expected": "passed" - }, - { - "fixture_id": "CTX-02", - "area": "context_projection", - "title": "Semantic authority is retained once and referenced by stable ID elsewhere", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_context_projection.py::test_ctx_02_semantic_authority_is_retained_once_and_referenced_by_id"], - "expected": "passed" - }, - { - "fixture_id": "CTX-03", - "area": "context_projection", - "title": "Executor capability projection contains only allocated capabilities and reasons", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_context_projection.py::test_ctx_03_executor_capability_projection_contains_only_allocated_capabilities"], - "expected": "passed" - }, - { - "fixture_id": "CTX-04", - "area": "context_projection", - "title": "Successful evidence projects compact digest and freshness receipts without history or stdout", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_context_projection.py::test_ctx_04_success_evidence_projects_compact_receipt_not_history_or_stdout"], - "expected": "passed" - }, - { - "fixture_id": "CTX-05", - "area": "context_projection", - "title": "Failed evidence expands only for an allowed explicit reason", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_context_projection.py::test_ctx_05_failed_evidence_expands_only_with_allowed_reason"], - "expected": "passed" - }, - { - "fixture_id": "CTX-06", - "area": "context_projection", - "title": "Compiled context has telemetry, lazy histories, no retrieval escape hatch, and no arbitrary hard limit", - "oracle": "pytest", - "pytest_nodes": ["tests/test_wor108_context_projection.py::test_ctx_06_no_retrieval_escape_hatch_and_context_metrics_use_existing_telemetry"], - "expected": "passed" - } - ] -} diff --git a/evals/wor108/migration-impact.json b/evals/wor108/migration-impact.json deleted file mode 100644 index 28b11cd..0000000 --- a/evals/wor108/migration-impact.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "contract": "wor108-migration-impact-v1", - "issue": "WOR-108", - "evaluation_id": "wor108-legacy-closure-v1", - "accepted_legacy_baseline": { - "commit": "9dce5df221485174d6179f713e8b179bbc20567a", - "tree": "5a1f38355eae8068bab528923e807ce54e6f6fe5" - }, - "changed_surfaces": { - "public_contracts": [ - "references/assets/orchestration/contract/handoff-executor-result-v1.md", - "references/assets/orchestration/contract/plan-v1.md", - "references/assets/orchestration/contract/stage-event-v1.schema.json", - "references/assets/orchestration/contract/stage-review-v1.schema.json", - "references/assets/orchestration/contract/task-v1.md", - "references/assets/orchestration/workflow.md" - ], - "instructions": [ - "references/assets/template/AGENTS.md", - "references/assets/template/bootstrap.yaml", - "references/assets/template/project.yaml", - "rules/orchestration/orch-artifact-authoring.md", - "rules/orchestration/orch-handoff-required.md", - "rules/work-bundle/wb-project-context-preflight.md", - "skills/dev-create-task-plan/SKILL.md", - "skills/orch-create-handoff/SKILL.md", - "skills/orch-create-implementation-plan/SKILL.md", - "skills/orch-create-specification/SKILL.md", - "skills/orch-doctor/SKILL.md", - "skills/orch-execute-plan/SKILL.md", - "skills/wb-initialize-project/SKILL.md" - ], - "operations": [ - "scripts/orchestration/dispatcher.py", - "scripts/orchestration/execution_context.py", - "scripts/orchestration/plans.py", - "scripts/orchestration/review_runtime.py", - "scripts/orchestration/task_ownership.py", - "scripts/work-bundle/control_plane.py", - "scripts/work-bundle/core.py", - "scripts/work-bundle/dispatcher.py", - "scripts/work-bundle/migration.py", - "scripts/work-bundle/project.py", - "scripts/work-bundle/reviewer_workspace.py", - "scripts/work-bundle/stage_events.py" - ], - "fixtures_and_evaluations": [ - "evals/wor105/components/native-transition-record.yaml", - "evals/wor105/freeze-manifest.json", - "evals/wor105/results.jsonl", - "evals/wor108/contracts-v1.schema.json", - "evals/wor108/fixtures.json", - "evals/wor108/migration-impact.json", - "evals/wor108/verify.py", - "evals/wor109/contracts-v1.schema.json", - "evals/wor109/fixtures.json", - "evals/wor109/migration-impact.json", - "evals/wor109/verify.py", - "references/evals/development/evals.json", - "references/evals/orchestration/evals.json", - "tests/test_control_plane_v4.py", - "tests/test_multi_repository_member.py", - "tests/test_orchestration_execution_context.py", - "tests/test_orchestration_reviews.py", - "tests/test_orchestration_skill_rule_boundary.py", - "tests/test_orchestration_workflow_contracts.py", - "tests/test_project_initialization.py", - "tests/test_registry_layout_migration.py", - "tests/test_wor105_native_transition.py", - "tests/test_wor108_closure.py", - "tests/test_wor108_context_projection.py", - "tests/test_wor108_review_frontier.py", - "tests/test_wor108_subagent_ownership.py", - "tests/test_wor109_accepted_result.py", - "tests/test_wor109_closure.py", - "tests/test_wor109_lifecycle.py", - "tests/test_wor109_lightweight.py", - "tests/test_wor109_planner_contracts.py", - "tests/test_wor109_planner_runtime.py" - ] - }, - "evaluation_identities": [ - "RF-01", "RF-02", "RF-03", "RF-04", "RF-05", "RF-06", "RF-07", "RF-08", - "SG-01", "SG-02", "SG-03", "SG-04", "SG-05", "SG-06", "SG-07", "SG-08", - "CTX-01", "CTX-02", "CTX-03", "CTX-04", "CTX-05", "CTX-06" - ], - "handoff_constraints": { - "final_git_identity": "post_acceptance_handoff_only", - "advance_wor107": false, - "touch_work_bundle_mcp": false - } -} diff --git a/evals/wor108/verify.py b/evals/wor108/verify.py deleted file mode 100644 index 38f98cb..0000000 --- a/evals/wor108/verify.py +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/env python3 -"""Verify the deterministic WOR-108 closure and migration-impact package.""" - -from __future__ import annotations - -import argparse -import json -from pathlib import Path -import re -import subprocess -from typing import Any - - -EVAL_ROOT = Path(__file__).resolve().parent -REPO_ROOT = EVAL_ROOT.parents[1] -EXPECTED_IDS = tuple( - [f"RF-{number:02d}" for number in range(1, 9)] - + [f"SG-{number:02d}" for number in range(1, 9)] - + [f"CTX-{number:02d}" for number in range(1, 7)] -) -EXPECTED_AREAS = { - "RF": "repair_frontier", - "SG": "subagent_ownership", - "CTX": "context_projection", -} -FIXTURE_KEYS = {"fixture_id", "area", "title", "oracle", "pytest_nodes", "expected"} -NODE = re.compile(r"^(tests/test_wor108_[a-z_]+\.py)::(test_[a-z0-9_]+)$") -MIGRATION_KEYS = { - "contract", "issue", "evaluation_id", "accepted_legacy_baseline", - "changed_surfaces", "evaluation_identities", "handoff_constraints", -} -SURFACE_KEYS = {"public_contracts", "instructions", "operations", "fixtures_and_evaluations"} - - -class VerificationError(RuntimeError): - pass - - -def _load(path: Path) -> dict[str, Any]: - value = json.loads(path.read_text(encoding="utf-8")) - if not isinstance(value, dict): - raise VerificationError(f"object required: {path}") - return value - - -def _verify_schema(path: Path) -> None: - schema = _load(path) - if schema.get("$id") != "urn:work-bundle:wor108:closure-contracts:v1": - raise VerificationError("closure schema identity mismatch") - if schema.get("additionalProperties") is not False: - raise VerificationError("closure schema must be closed") - fixture = schema.get("$defs", {}).get("fixture", {}) - fixture_id = fixture.get("properties", {}).get("fixture_id", {}) - if ( - fixture.get("type") != "object" - or fixture.get("additionalProperties") is not False - or set(fixture.get("required", [])) != FIXTURE_KEYS - or fixture_id.get("type") != "string" - ): - raise VerificationError("fixture schema is not a closed required shape") - - -def _verify_fixtures(path: Path) -> tuple[str, ...]: - package = _load(path) - if set(package) != {"contract", "evaluation_id", "fixtures"}: - raise VerificationError("fixture package closed shape mismatch") - if package["contract"] != "wor108-closure-fixtures-v1" or package["evaluation_id"] != "wor108-legacy-closure-v1": - raise VerificationError("fixture package identity mismatch") - fixtures = package["fixtures"] - if not isinstance(fixtures, list): - raise VerificationError("fixtures must be an array") - fixture_ids = tuple(item.get("fixture_id") for item in fixtures if isinstance(item, dict)) - if fixture_ids != EXPECTED_IDS: - raise VerificationError("fixture IDs must be exactly RF-01..08, SG-01..08, CTX-01..06 in order") - seen_nodes: set[str] = set() - for item in fixtures: - if set(item) != FIXTURE_KEYS: - raise VerificationError(f"fixture closed shape mismatch: {item.get('fixture_id')}") - fixture_id = item["fixture_id"] - if item["area"] != EXPECTED_AREAS[fixture_id.split("-", 1)[0]]: - raise VerificationError(f"fixture area mismatch: {fixture_id}") - if item["oracle"] != "pytest" or item["expected"] != "passed" or not item["title"]: - raise VerificationError(f"fixture oracle mismatch: {fixture_id}") - nodes = item["pytest_nodes"] - if not isinstance(nodes, list) or not nodes or len(nodes) != len(set(nodes)): - raise VerificationError(f"fixture pytest node set mismatch: {fixture_id}") - for node in nodes: - match = NODE.fullmatch(node) - if match is None: - raise VerificationError(f"invalid pytest node: {node}") - test_path = REPO_ROOT / match.group(1) - if not test_path.is_file() or f"def {match.group(2)}(" not in test_path.read_text(encoding="utf-8"): - raise VerificationError(f"pytest oracle does not resolve: {node}") - if node in seen_nodes: - raise VerificationError(f"pytest oracle assigned to multiple identities: {node}") - seen_nodes.add(node) - return fixture_ids - - -def _verify_migration(path: Path, fixture_ids: tuple[str, ...]) -> int: - manifest = _load(path) - if set(manifest) != MIGRATION_KEYS: - raise VerificationError("migration-impact closed shape mismatch") - if (manifest["contract"], manifest["issue"], manifest["evaluation_id"]) != ( - "wor108-migration-impact-v1", "WOR-108", "wor108-legacy-closure-v1" - ): - raise VerificationError("migration-impact identity mismatch") - if manifest["accepted_legacy_baseline"] != { - "commit": "9dce5df221485174d6179f713e8b179bbc20567a", - "tree": "5a1f38355eae8068bab528923e807ce54e6f6fe5", - }: - raise VerificationError("accepted WOR-105 baseline mismatch") - surfaces = manifest["changed_surfaces"] - if not isinstance(surfaces, dict) or set(surfaces) != SURFACE_KEYS: - raise VerificationError("changed surface classes mismatch") - flattened = [surface for group in surfaces.values() for surface in group] - if len(flattened) != len(set(flattened)) or any(not (REPO_ROOT / surface).is_file() for surface in flattened): - raise VerificationError("changed surfaces must be unique existing files") - baseline = manifest["accepted_legacy_baseline"]["commit"] - changed = subprocess.run( - ["git", "-C", str(REPO_ROOT), "diff", "--name-only", baseline, "HEAD", "--"], - check=False, - capture_output=True, - text=True, - ) - if changed.returncode != 0: - raise VerificationError("accepted baseline delta is unavailable") - if set(flattened) != set(changed.stdout.splitlines()): - raise VerificationError("changed surface completeness mismatch") - if tuple(manifest["evaluation_identities"]) != fixture_ids: - raise VerificationError("migration evaluation identities mismatch") - if manifest["handoff_constraints"] != { - "final_git_identity": "post_acceptance_handoff_only", - "advance_wor107": False, - "touch_work_bundle_mcp": False, - }: - raise VerificationError("migration handoff constraints mismatch") - forbidden_identity_fields = {"accepted_head", "accepted_tree", "final_commit", "final_tree"} - if forbidden_identity_fields.intersection(manifest): - raise VerificationError("self-referential final Git identity is forbidden") - return len(flattened) - - -def verify( - fixtures_path: Path = EVAL_ROOT / "fixtures.json", - migration_path: Path = EVAL_ROOT / "migration-impact.json", - schema_path: Path = EVAL_ROOT / "contracts-v1.schema.json", -) -> dict[str, Any]: - _verify_schema(schema_path) - fixture_ids = _verify_fixtures(fixtures_path) - surfaces = _verify_migration(migration_path, fixture_ids) - return { - "evaluation_id": "wor108-legacy-closure-v1", - "fixtures": len(fixture_ids), - "changed_surfaces": surfaces, - "verdict": "accepted", - } - - -def main() -> int: - parser = argparse.ArgumentParser() - parser.add_argument("--fixtures", type=Path, default=EVAL_ROOT / "fixtures.json") - parser.add_argument("--migration-impact", type=Path, default=EVAL_ROOT / "migration-impact.json") - parser.add_argument("--schema", type=Path, default=EVAL_ROOT / "contracts-v1.schema.json") - args = parser.parse_args() - print(json.dumps(verify(args.fixtures, args.migration_impact, args.schema), sort_keys=True)) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/scripts/orchestration/core.py b/scripts/orchestration/core.py index 2942c1a..f835445 100644 --- a/scripts/orchestration/core.py +++ b/scripts/orchestration/core.py @@ -220,6 +220,38 @@ def orchestration_root(args: argparse.Namespace) -> Path: return work_bundle(args) / "orchestration" +def resolve_execution_artifact_path( + workspace_root: Path, + *, + execution_id: str, + artifact_path: str, + source_members: list[Path], +) -> Path: + """Resolve run-owned output under the containing workspace, never a source member.""" + + if not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9._:-]*", execution_id): + raise SystemExit("Execution artifact path requires a valid execution id") + relative = Path(artifact_path) + if ( + not artifact_path.strip() + or relative.is_absolute() + or not relative.parts + or any(part in {"", ".", ".."} for part in relative.parts) + ): + raise SystemExit("Execution artifact path must be a canonical relative path") + target = ( + workspace_root.expanduser().resolve() + / "orchestration" + / "executions" + / execution_id + / relative + ).resolve() + for source_member in source_members: + if is_relative_to(target, source_member.expanduser().resolve()): + raise SystemExit("Execution artifact path resolves inside a source member") + return target + + def ensure_under_orchestration(path: Path, args: argparse.Namespace) -> Path: resolved = path.resolve() allowed = orchestration_root(args).resolve() diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index 870cc62..2ee0bcc 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -4135,7 +4135,23 @@ def _assert_no_source_local_execution_artifacts(task: dict[str, Any], task_path: path = canonical_relative_path(str(value)) except OwnershipBlocker: continue - if path == "orchestration/executions" or path.startswith("orchestration/executions/"): + parts = Path(path).parts + issue_eval = ( + len(parts) >= 2 + and parts[0] == "evals" + and re.fullmatch(r"(?:wor|issue)[-_]?\d+", parts[1], re.IGNORECASE) + ) + issue_test = ( + len(parts) == 2 + and parts[0] == "tests" + and re.match(r"test_(?:wor|issue)[-_]?\d+(?:_|\.py)", parts[1], re.IGNORECASE) + ) + if ( + path == "orchestration/executions" + or path.startswith("orchestration/executions/") + or issue_eval + or issue_test + ): raise SystemExit( f"Task write scope uses a source-local execution artifact path: {task_path}: {path}" ) diff --git a/tests/test_ci_release_gate.py b/tests/test_ci_release_gate.py index 5280548..3eb340f 100644 --- a/tests/test_ci_release_gate.py +++ b/tests/test_ci_release_gate.py @@ -1,6 +1,7 @@ from __future__ import annotations import runpy +import re import subprocess from pathlib import Path @@ -44,7 +45,7 @@ def fake_run(command, **kwargs): assert "WB_CI_MODULE PASS tests/test_c.py" in output -def test_release_gate_inputs_are_tracked_and_control_plane_independent() -> None: +def test_release_gate_inputs_are_tracked_and_execution_independent() -> None: tracked = subprocess.run( ["git", "ls-files", "tests/test_*.py"], cwd=REPO_ROOT, @@ -55,12 +56,7 @@ def test_release_gate_inputs_are_tracked_and_control_plane_independent() -> None discovered = sorted(path.relative_to(REPO_ROOT).as_posix() for path in (REPO_ROOT / "tests").glob("test_*.py")) assert tracked == discovered - for path in [ - CI_ENTRY, - REPO_ROOT / "bin" / "work-bundle-skill", - REPO_ROOT / ".github" / "workflows" / "ci.yml", - REPO_ROOT / "evals" / "wor105" / "components" / "native-transition-record.yaml", - ]: + for path in [CI_ENTRY, REPO_ROOT / "bin" / "work-bundle-skill", REPO_ROOT / ".github" / "workflows" / "ci.yml"]: subprocess.run( ["git", "ls-files", "--error-unmatch", path.relative_to(REPO_ROOT).as_posix()], cwd=REPO_ROOT, @@ -69,16 +65,15 @@ def test_release_gate_inputs_are_tracked_and_control_plane_independent() -> None text=True, ) assert ".work-bundle" not in CI_ENTRY.read_text(encoding="utf-8") + assert not (REPO_ROOT / "evals" / "wor105").exists() + assert not (REPO_ROOT / "evals" / "wor108").exists() + assert not any(re.match(r"test_(?:wor|issue)[-_]?\d+", Path(path).stem) for path in discovered) -def test_release_oracles_reject_developer_only_control_evidence() -> None: - transition_source = (REPO_ROOT / "tests" / "test_wor105_native_transition.py").read_text(encoding="utf-8") - - assert 'TRANSITION_RECORD = REPO_ROOT / "evals" / "wor105" / "components"' in transition_source - assert "WORKSPACE_ROOT = REPO_ROOT.parent" not in transition_source - assert "ORCHESTRATION =" not in transition_source - assert '"handoff" / "executor"' not in transition_source - assert "review_path.read_bytes" not in transition_source +def test_release_gate_does_not_read_workspace_execution_evidence() -> None: + source = CI_ENTRY.read_text(encoding="utf-8") + assert "orchestration/executions" not in source + assert "evals/wor" not in source def test_release_gate_collects_test_and_skill_failures() -> None: @@ -133,10 +128,10 @@ def test_workflow_delegates_to_canonical_release_gate() -> None: assert pin in entry -def test_workflow_fetches_full_history_for_historical_transition_identity() -> None: +def test_workflow_uses_default_checkout_history_for_current_project_tests() -> None: workflow = yaml.safe_load((REPO_ROOT / ".github" / "workflows" / "ci.yml").read_text(encoding="utf-8")) steps = workflow["jobs"]["deterministic"]["steps"] checkout = [step for step in steps if step.get("uses", "").startswith("actions/checkout@")] assert len(checkout) == 1 - assert checkout[0].get("with", {}).get("fetch-depth") == 0 + assert "with" not in checkout[0] diff --git a/tests/test_execution_artifact_placement.py b/tests/test_execution_artifact_placement.py new file mode 100644 index 0000000..9143349 --- /dev/null +++ b/tests/test_execution_artifact_placement.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +import sys +from pathlib import Path + +import pytest + + +REPO_ROOT = Path(__file__).resolve().parents[1] +ORCHESTRATION = REPO_ROOT / "scripts" / "orchestration" +sys.path.insert(0, str(ORCHESTRATION)) + +import execution_context # noqa: E402 +from core import resolve_execution_artifact_path # noqa: E402 + + +@pytest.mark.parametrize( + "path", + [ + "evals/wor112/result.json", + "evals/issue-112/manifest.json", + "tests/test_wor112_acceptance.py", + "tests/test_issue_112_evidence.py", + "orchestration/executions/plan-001/result.json", + ], +) +def test_static_admission_rejects_issue_run_artifacts_in_source(path: str) -> None: + task = {"files": {"write": [path]}} + with pytest.raises(SystemExit, match="source-local execution artifact"): + execution_context._assert_no_source_local_execution_artifacts(task, Path("task.md")) + + +def test_execution_artifacts_resolve_to_workspace_root_outside_source_member(tmp_path: Path) -> None: + workspace = tmp_path / "workspace" + source = workspace / "work-bundle-main" + source.mkdir(parents=True) + + target = resolve_execution_artifact_path( + workspace, + execution_id="plan-20260908-001", + artifact_path="evidence/task-004.json", + source_members=[source], + ) + + assert target == workspace / "orchestration/executions/plan-20260908-001/evidence/task-004.json" + assert not target.is_relative_to(source) + + +@pytest.mark.parametrize("artifact_path", ["../escape.json", "/tmp/escape.json", "."]) +def test_execution_artifact_resolution_rejects_unsafe_paths( + tmp_path: Path, artifact_path: str +) -> None: + with pytest.raises(SystemExit, match="artifact path"): + resolve_execution_artifact_path( + tmp_path, + execution_id="plan-001", + artifact_path=artifact_path, + source_members=[tmp_path / "source"], + ) + + +def test_execution_artifact_resolution_fails_when_workspace_output_is_inside_source( + tmp_path: Path, +) -> None: + with pytest.raises(SystemExit, match="source member"): + resolve_execution_artifact_path( + tmp_path, + execution_id="plan-001", + artifact_path="result.json", + source_members=[tmp_path], + ) diff --git a/tests/test_orchestration_accepted_result_lifecycle.py b/tests/test_orchestration_accepted_result_lifecycle.py index ec4a1e4..3d1afa3 100644 --- a/tests/test_orchestration_accepted_result_lifecycle.py +++ b/tests/test_orchestration_accepted_result_lifecycle.py @@ -328,20 +328,3 @@ def test_recovery_commands_are_not_public_dispatcher_actions() -> None: assert "adopt-existing-recovered-result" not in dispatcher.RECOGNIZED_COMMANDS with pytest.raises(SystemExit): dispatcher.build_parser().parse_args(["create-accepted-base-absence-receipt"]) - - -def test_wor105_historical_identity_and_release_anchor_are_separate() -> None: - import yaml - - record = yaml.safe_load( - (REPO_ROOT / "evals/wor105/components/native-transition-record.yaml").read_text( - encoding="utf-8" - ) - ) - - assert record["accepted_commit"] == "9dce5df221485174d6179f713e8b179bbc20567a" - assert record["accepted_tree"] == "5a1f38355eae8068bab528923e807ce54e6f6fe5" - assert record["release_anchor"] == { - "commit": "cfa089f0d2ed211b98d049eb37bfcdccb8091516", - "tree": "12e4a696c3caf991654f0b9ac9ef40594699c8d4", - } diff --git a/tests/test_wor108_context_projection.py b/tests/test_orchestration_context_projection.py similarity index 87% rename from tests/test_wor108_context_projection.py rename to tests/test_orchestration_context_projection.py index 7b99f6f..cad9c67 100644 --- a/tests/test_wor108_context_projection.py +++ b/tests/test_orchestration_context_projection.py @@ -437,213 +437,6 @@ def test_repair_acceptance_requires_exact_runtime_owner_and_continuity( ) -def test_accepted_dependency_deltas_use_exact_handoff_and_observed_checkpoint( - tmp_path: Path, -) -> None: - root, _, task = workspace(tmp_path) - task.write_text( - task.read_text(encoding="utf-8").replace( - "phase_id: phase-001\n", - "phase_id: phase-001\ndepends_on: [task-dependency]\n", - ), - encoding="utf-8", - ) - scoped = _ensure_source_file(root) - dependency = root / "references/assets/orchestration/workflow.md" - dependency.parent.mkdir(parents=True, exist_ok=True) - dependency.write_text("before\n", encoding="utf-8") - git(root, "add", ".") - git(root, "commit", "-qm", "baseline") - baseline = git(root, "rev-parse", "HEAD") - baseline_tree = git(root, "rev-parse", "HEAD^{tree}") - - brief = _document(root, task)["task_brief"] - _bind_task_execution(root, brief) - - dependency.write_text("accepted dependency\n", encoding="utf-8") - git(root, "add", str(dependency.relative_to(root))) - git(root, "commit", "-qm", "accepted dependency") - checkpoint = git(root, "rev-parse", "HEAD") - checkpoint_tree = git(root, "rev-parse", "HEAD^{tree}") - scoped.write_text("def compile_task():\n return 'task repair'\n", encoding="utf-8") - git(root, "add", str(scoped.relative_to(root))) - git(root, "commit", "-qm", "task repair") - - identity = lambda commit, tree: { - "artifact_id": "task-dependency", - "revision": commit, - "sha256": execution_context.semantic_digest({"commit": commit, "tree": tree}), - "source_tree": tree, - } - dependency_handoff = { - "id": "handoff-accepted-dependency", - "type": "executor-result", - "related": {"plan": brief["plan_id"], "task": "task-dependency"}, - "result": {"state": "completed"}, - "acceptance_review": { - "required": True, - "verdict": "accept", - "review_mode": "repair", - "target_identity": identity(checkpoint, checkpoint_tree), - "repair_frontier": { - "previous_reviewed_identity": identity(baseline, baseline_tree), - "repaired_identity": identity(checkpoint, checkpoint_tree), - }, - }, - } - accepted_path = ( - root - / ".work-bundle/orchestration/handoff/executor/active/handoff-accepted-dependency.yaml" - ) - accepted_path.parent.mkdir(parents=True, exist_ok=True) - accepted_path.write_text( - "\n".join(execution_context._dump_yaml(dependency_handoff)) + "\n", - encoding="utf-8", - ) - descriptor = { - "task_id": "task-dependency", - "handoff_id": "handoff-accepted-dependency", - "handoff_sha256": hashlib.sha256(accepted_path.read_bytes()).hexdigest(), - "integrated_base": baseline, - "integrated_head": checkpoint, - } - - current = _without_terminal_evidence(brief, "Dependency attribution fixture.") - current["evidence_applicability"] = { - "metadata": {"required": False, "reasons": []}, - "repository": {"required": True, "reasons": ["accepted dependency delta"]}, - "codegraph": {"required": False, "reasons": []}, - } - handoff = { - "type": "executor-result", - "related": {"plan": brief["plan_id"], "task": brief["task_id"]}, - "result": {"state": "completed"}, - "task_fit_check": {"task": brief["task_id"], "result": "clean"}, - "delegation_evidence": _delegation_evidence(), - "repository": [{ - "root": str(root.resolve()), - "target_kind": "git-backed", - "preflight_kind": "git-clean-worktree", - "baseline": "initial", - "status": "clean", - }], - "knowledge_disposition": { - "action": "none", - "reason": "No stable authority changed.", - "affected_authority": [], - }, - } - - with pytest.raises(SystemExit, match="accepted dependency task authority"): - execution_context.validate_executor_result_for_task( - handoff, current, observe=True, mutation_events=[] - ) - accepted = execution_context.validate_executor_result_for_task( - handoff, - current, - observe=True, - mutation_events=[], - accepted_dependency_deltas=[descriptor], - ) - assert accepted["result_state"] == "completed" - - cli_handoff = deepcopy(handoff) - cli_handoff["result"]["state"] = "partial" - cli_handoff["changes"] = { - "files": [ - { - "path": "scripts/orchestration/execution_context.py", - "action": "modified", - } - ] - } - cli_handoff["codegraph"] = [ - { - "root": str(root.resolve()), - "applicable": False, - "up_to_date": False, - "reason": "no-index", - } - ] - cli_handoff_path = ( - root / ".work-bundle/orchestration/handoff/executor/active/handoff-task-004.yaml" - ) - cli_handoff_path.write_text( - "\n".join(execution_context._dump_yaml(cli_handoff)) + "\n", encoding="utf-8" - ) - cli = subprocess.run( - [ - sys.executable, - str(REPO_ROOT / "scripts/orch.py"), - "build-review-package", - "--project-root", - str(root), - "--task", - str(task), - "--handoff", - str(cli_handoff_path), - "--base", - baseline, - "--head", - git(root, "rev-parse", "HEAD"), - "--accepted-dependency-deltas", - json.dumps([descriptor]), - ], - capture_output=True, - text=True, - ) - assert cli.returncode == 0, cli.stderr - assert "review-package.md" in cli.stdout - - accepted_bytes = accepted_path.read_bytes() - wrong_plan_handoff = deepcopy(dependency_handoff) - wrong_plan_handoff["related"]["plan"] = "plan-WRONG" - accepted_path.write_text( - "\n".join(execution_context._dump_yaml(wrong_plan_handoff)) + "\n", - encoding="utf-8", - ) - wrong_plan = { - **descriptor, - "handoff_sha256": hashlib.sha256(accepted_path.read_bytes()).hexdigest(), - } - with pytest.raises(SystemExit, match="plan"): - execution_context.validate_executor_result_for_task( - handoff, current, observe=True, accepted_dependency_deltas=[wrong_plan] - ) - accepted_path.write_bytes(accepted_bytes) - - stale = {**descriptor, "handoff_sha256": "0" * 64} - with pytest.raises(SystemExit, match="handoff identity is stale"): - execution_context.validate_executor_result_for_task( - handoff, current, observe=True, accepted_dependency_deltas=[stale] - ) - mismatched = {**descriptor, "integrated_head": git(root, "rev-parse", "HEAD")} - with pytest.raises(SystemExit, match="checkpoint is mismatched"): - execution_context.validate_executor_result_for_task( - handoff, current, observe=True, accepted_dependency_deltas=[mismatched] - ) - unaccepted_handoff = deepcopy(dependency_handoff) - unaccepted_handoff["acceptance_review"]["verdict"] = "pending" - accepted_path.write_text( - "\n".join(execution_context._dump_yaml(unaccepted_handoff)) + "\n", - encoding="utf-8", - ) - unaccepted = { - **descriptor, - "handoff_sha256": hashlib.sha256(accepted_path.read_bytes()).hexdigest(), - } - with pytest.raises(SystemExit, match="not an accepted repair result"): - execution_context.validate_executor_result_for_task( - handoff, current, observe=True, accepted_dependency_deltas=[unaccepted] - ) - accepted_path.write_bytes(accepted_bytes) - dependency.write_text("later task mutation\n", encoding="utf-8") - with pytest.raises(SystemExit, match="changed after integration"): - execution_context.validate_executor_result_for_task( - handoff, current, observe=True, accepted_dependency_deltas=[descriptor] - ) - - def test_accepted_dependency_repair_chain_is_ordered_contiguous_and_last_wins( tmp_path: Path, ) -> None: diff --git a/tests/test_wor108_review_frontier.py b/tests/test_orchestration_review_frontier.py similarity index 100% rename from tests/test_wor108_review_frontier.py rename to tests/test_orchestration_review_frontier.py diff --git a/tests/test_wor108_subagent_ownership.py b/tests/test_orchestration_subagent_ownership.py similarity index 100% rename from tests/test_wor108_subagent_ownership.py rename to tests/test_orchestration_subagent_ownership.py diff --git a/tests/test_wor105_evals.py b/tests/test_wor105_evals.py deleted file mode 100644 index 0235b11..0000000 --- a/tests/test_wor105_evals.py +++ /dev/null @@ -1,161 +0,0 @@ -from __future__ import annotations - -import importlib.util -import hashlib -import json -from pathlib import Path -import subprocess - -import pytest - - -REPO_ROOT = Path(__file__).resolve().parents[1] -EVAL_ROOT = REPO_ROOT / "evals" / "wor105" - - -def _load(name: str, filename: str): - path = EVAL_ROOT / filename - spec = importlib.util.spec_from_file_location(name, path) - if spec is None or spec.loader is None: - raise RuntimeError(f"cannot load {path}") - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - return module - - -def _manifest_for_current_runner(tmp_path: Path) -> Path: - manifest = json.loads((EVAL_ROOT / "freeze-manifest.json").read_text(encoding="utf-8")) - manifest["components"]["runner"]["sha256"] = hashlib.sha256( - (EVAL_ROOT / "run.py").read_bytes() - ).hexdigest() - target = tmp_path / "current-runner-manifest.json" - target.write_text(json.dumps(manifest), encoding="utf-8") - return target - - -def test_frozen_manifest_components_are_repository_local() -> None: - manifest = json.loads((EVAL_ROOT / "freeze-manifest.json").read_text(encoding="utf-8")) - - for name, component in manifest["components"].items(): - if name == "fixtures": - continue - target = (REPO_ROOT / component["path"]).resolve() - assert target.is_relative_to(REPO_ROOT), name - assert target.is_file(), name - - -def test_frozen_manifest_runs_and_independent_verifier_accepts_twelve(tmp_path: Path) -> None: - runner = _load("wor105_runner", "run.py") - verifier = _load("wor105_verifier", "verify.py") - output = tmp_path / "results.jsonl" - manifest_path = _manifest_for_current_runner(tmp_path) - - results = runner.run_manifest(manifest_path, output) - summary = verifier.verify_results(manifest_path, output) - - assert len(results) == 12 - assert summary == {"passed": 12, "total": 12, "verdict": "accepted"} - adv01 = results[0] - assert len(adv01["proof"]["denial_classes"]) == 5 - assert len(set(adv01["proof"]["event_ids"])) == 2 - - -def test_independent_verifier_rejects_missing_proof_and_relation_drift(tmp_path: Path) -> None: - runner = _load("wor105_runner_mutation", "run.py") - verifier = _load("wor105_verifier_mutation", "verify.py") - output = tmp_path / "results.jsonl" - manifest_path = _manifest_for_current_runner(tmp_path) - runner.run_manifest(manifest_path, output) - rows = [json.loads(line) for line in output.read_text(encoding="utf-8").splitlines()] - - rows[0]["proof"].pop("validator_output_sha256") - output.write_text("\n".join(json.dumps(row, sort_keys=True) for row in rows) + "\n", encoding="utf-8") - with pytest.raises(verifier.VerificationError, match="schema required fields"): - verifier.verify_results(manifest_path, output) - - runner.run_manifest(manifest_path, output) - rows = [json.loads(line) for line in output.read_text(encoding="utf-8").splitlines()] - rows[7]["proof"]["reuse_of"] = "different-observation" - rows[7]["adjudication_sha256"] = hashlib.sha256( - json.dumps(rows[7]["proof"], sort_keys=True, separators=(",", ":")).encode("utf-8") - ).hexdigest() - output.write_text("\n".join(json.dumps(row, sort_keys=True) for row in rows) + "\n", encoding="utf-8") - with pytest.raises(verifier.VerificationError, match="ADV-08 reuse"): - verifier.verify_results(manifest_path, output) - - -def test_verifier_rejects_fixture_or_evaluator_identity_drift(tmp_path: Path) -> None: - runner = _load("wor105_runner_identity", "run.py") - verifier = _load("wor105_verifier_identity", "verify.py") - output = tmp_path / "results.jsonl" - manifest_path = _manifest_for_current_runner(tmp_path) - runner.run_manifest(manifest_path, output) - manifest = json.loads(manifest_path.read_text(encoding="utf-8")) - manifest["components"]["fixtures"]["sha256"] = "0" * 64 - drifted = tmp_path / "manifest.json" - drifted.write_text(json.dumps(manifest), encoding="utf-8") - - with pytest.raises(verifier.VerificationError, match="fixture aggregate"): - verifier.verify_results(drifted, output) - - -def test_verifier_enforces_normative_result_schema_types(tmp_path: Path) -> None: - runner = _load("wor105_runner_schema", "run.py") - verifier = _load("wor105_verifier_schema", "verify.py") - output = tmp_path / "results.jsonl" - manifest_path = _manifest_for_current_runner(tmp_path) - runner.run_manifest(manifest_path, output) - rows = [json.loads(line) for line in output.read_text(encoding="utf-8").splitlines()] - rows[7]["proof"]["subprocess_invocation_count"] = "1" - output.write_text("\n".join(json.dumps(row, sort_keys=True) for row in rows) + "\n", encoding="utf-8") - - with pytest.raises(verifier.VerificationError, match="schema constant"): - verifier.verify_results(manifest_path, output) - - -def test_runner_does_not_import_verifier_or_copy_expected_as_decision() -> None: - source = (EVAL_ROOT / "run.py").read_text(encoding="utf-8") - assert "import verify" not in source - assert 'actual_decision = fixture["expected_decision"]' not in source - - -def test_runner_invokes_one_native_probe_for_every_fixture(tmp_path: Path, monkeypatch) -> None: - runner = _load("wor105_runner_native_calls", "run.py") - calls: list[list[str]] = [] - - def observed_run(command, **kwargs): - calls.append(list(command)) - return subprocess.CompletedProcess(command, 0, stdout="1 passed\n", stderr="") - - monkeypatch.setattr(runner.subprocess, "run", observed_run) - results = runner.run_manifest(_manifest_for_current_runner(tmp_path), tmp_path / "results.jsonl") - - assert len(calls) == 12 - assert all(command[:4] == ["uvx", "--python", "3.13", "--from"] for command in calls) - assert all(item["passed"] for item in results) - - -def test_runner_rejects_a_probe_that_reports_zero_native_invocations(tmp_path: Path, monkeypatch) -> None: - runner = _load("wor105_runner_zero_calls", "run.py") - monkeypatch.setattr( - runner, - "_run_native_probe", - lambda fixture_id: runner.NativeProbe(fixture_id, 0, "", ""), - ) - - with pytest.raises(runner.EvaluationError, match="zero native invocations"): - runner.run_manifest(_manifest_for_current_runner(tmp_path), tmp_path / "results.jsonl") - - -def test_native_probe_digest_ignores_nondeterministic_pytest_timing(monkeypatch) -> None: - runner = _load("wor105_runner_stable_probe", "run.py") - outputs = iter(["1 passed in 0.11s\n", "1 passed in 0.87s\n"]) - - def observed_run(command, **kwargs): - return subprocess.CompletedProcess(command, 0, stdout=next(outputs), stderr="") - - monkeypatch.setattr(runner.subprocess, "run", observed_run) - first = runner._run_native_probe("ADV-01") - second = runner._run_native_probe("ADV-01") - - assert first.output_sha256 == second.output_sha256 diff --git a/tests/test_wor105_native_dogfood.py b/tests/test_wor105_native_dogfood.py deleted file mode 100644 index a347c84..0000000 --- a/tests/test_wor105_native_dogfood.py +++ /dev/null @@ -1,173 +0,0 @@ -from __future__ import annotations - -import hashlib -import json -import os -from pathlib import Path -import subprocess -import sys - -import yaml - - -REPO_ROOT = Path(__file__).resolve().parents[1] -CONTROL_ROOT = REPO_ROOT.parent / ".work-bundle" -MCP_ROOT = REPO_ROOT.parents[2] / "work-bundle-mcp" -TRANSITION_RECORD = REPO_ROOT / "evals/wor105/components/native-transition-record.yaml" - - -NATIVE_GROUPS = ( - ( - "review-routing", - "tests/test_orchestration_reviews.py::test_api_001_rejects_unclassified_wrong_layer_and_unauthorized_blocking_advisory", - "tests/test_orchestration_reviews.py::test_api_001_reslice_pauses_repeated_expansion_and_preserves_evidence", - "tests/test_orchestration_reviews.py::test_api_002_requires_independent_direct_accepted_review_and_current_target", - ), - ( - "reviewer-isolation", - "tests/test_reviewer_workspace.py::test_bounded_read_search_and_validators_are_allowed", - "tests/test_reviewer_workspace.py::test_reviewer_operations_mechanically_deny_forbidden_effects", - "tests/test_reviewer_workspace.py::test_every_denied_request_appends_unique_privacy_safe_event", - ), - ( - "evaluation-identity", - "tests/test_orchestration_evaluations.py::test_pre_invocation_freeze_computes_exact_sources_and_excludes_result_fields", - "tests/test_orchestration_evaluations.py::test_component_drift_marks_stale_appends_and_preserves_raw", - "tests/test_orchestration_evaluations.py::test_packaging_only_advance_preserves_source_observation", - ), - ( - "completion-provenance", - "tests/test_completion_provenance.py::test_observation_reuse_requires_complete_identity_and_freshness", - "tests/test_completion_provenance.py::test_predecessor_extension_uses_public_contract_not_byte_identity", - "tests/test_completion_provenance.py::test_kernel_failure_owner_lifecycle_preserves_origin_and_blocks_early_release", - "tests/test_completion_provenance.py::test_failure_resume_and_release_preserve_first_owner_and_emit_native_events", - ), - ( - "stage-events", - "tests/test_stage_events.py::test_api_004_privacy_filter_fails_closed_without_echoing_content", - "tests/test_stage_events.py::test_api_004_append_is_prefix_preserving_unique_and_monotonic", - "tests/test_stage_events.py::test_api_004_query_and_export_preserve_order_and_do_not_mutate", - ), - ( - "deferred-remote", - "tests/test_multi_repository_member.py::test_deferred_remote_apply_replay_and_attach_are_portable_and_idempotent", - "tests/test_multi_repository_member.py::test_deferred_remote_composite_attach_rejects_external_git_common_dir", - ), - ( - "semantic-capabilities", - "tests/test_capability_index.py::test_retrieval_filters_authority_lifecycle_and_freshness", - "tests/test_capability_index.py::test_retrieval_uses_aliases_and_optional_domain_hints", - "tests/test_capability_index.py::test_traversal_enforces_node_budget_and_records_frontier", - ), -) - - -def _git(root: Path, *args: str) -> str: - return subprocess.run( - ["git", "-C", str(root), *args], text=True, capture_output=True, check=True - ).stdout.strip() - - -def _repository_identity(root: Path) -> dict[str, str] | None: - if not (root / ".git").exists(): - return None - return { - "head": _git(root, "rev-parse", "HEAD"), - "tree": _git(root, "rev-parse", "HEAD^{tree}"), - "status": _git(root, "status", "--porcelain=v1", "--untracked-files=all"), - } - - -def _control_digest() -> str: - digest = hashlib.sha256() - for path in sorted((CONTROL_ROOT / "orchestration").rglob("*")): - if path.is_file() and not path.is_symlink(): - digest.update(path.relative_to(CONTROL_ROOT).as_posix().encode()) - digest.update(b"\0") - digest.update(path.read_bytes()) - digest.update(b"\n") - return digest.hexdigest() - - -def native_evidence_chain() -> list[dict[str, str | int]]: - environment = {**os.environ, "PYTHONDONTWRITEBYTECODE": "1"} - evidence: list[dict[str, str | int]] = [] - for group in NATIVE_GROUPS: - completed = subprocess.run( - [sys.executable, "-m", "pytest", "-q", *group[1:]], - cwd=REPO_ROOT, - env=environment, - text=True, - capture_output=True, - check=False, - ) - output = completed.stdout + completed.stderr - if completed.returncode != 0: - raise AssertionError(f"native group {group[0]} failed:\n{output}") - evidence.append( - { - "group": group[0], - "exit_code": completed.returncode, - "output_sha256": hashlib.sha256(output.encode()).hexdigest(), - } - ) - verified = subprocess.run( - [ - sys.executable, - "evals/wor105/verify.py", - "--manifest", - "evals/wor105/freeze-manifest.json", - "--results", - "evals/wor105/results.jsonl", - ], - cwd=REPO_ROOT, - env=environment, - text=True, - capture_output=True, - check=False, - ) - assert verified.returncode == 0, verified.stdout + verified.stderr - assert json.loads(verified.stdout) == {"passed": 12, "total": 12, "verdict": "accepted"} - evidence.append( - { - "group": "adversarial-replay", - "exit_code": verified.returncode, - "output_sha256": hashlib.sha256(verified.stdout.encode()).hexdigest(), - } - ) - return evidence - - -def native_dogfood_lifecycle() -> dict[str, object]: - transition = yaml.safe_load( - TRANSITION_RECORD.read_text(encoding="utf-8") - ) - assert transition["enforcement_transition"] == "bootstrap_policy_to_native" - assert transition["transition_task"] == "task-b06r" - assert transition["excluded_work"] == ["WOR-66", "WOR-79", "WOR-107", "work-bundle-mcp mutation"] - - source_before = _repository_identity(REPO_ROOT) - mcp_before = _repository_identity(MCP_ROOT) - control_before = _control_digest() - evidence = native_evidence_chain() - assert _repository_identity(REPO_ROOT) == source_before - assert _repository_identity(MCP_ROOT) == mcp_before - assert _control_digest() == control_before - assert len(evidence) == 8 and all(item["exit_code"] == 0 for item in evidence) - return { - "enforcement_mode": "native", - "transition_tree": transition["accepted_tree"], - "evidence": evidence, - "excluded_work_preserved": True, - } - - -def test_native_dogfood_lifecycle() -> None: - result = native_dogfood_lifecycle() - assert result["enforcement_mode"] == "native" - assert result["excluded_work_preserved"] is True - - -def test_native_dogfood_transition_evidence_is_repository_local() -> None: - assert TRANSITION_RECORD == REPO_ROOT / "evals/wor105/components/native-transition-record.yaml" - assert TRANSITION_RECORD.is_file() diff --git a/tests/test_wor105_native_transition.py b/tests/test_wor105_native_transition.py deleted file mode 100644 index 4ec49fb..0000000 --- a/tests/test_wor105_native_transition.py +++ /dev/null @@ -1,136 +0,0 @@ -from __future__ import annotations - -import re -import subprocess -from copy import deepcopy -from pathlib import Path - -import pytest -import yaml - - -REPO_ROOT = Path(__file__).resolve().parents[1] -TRANSITION_RECORD = REPO_ROOT / "evals" / "wor105" / "components" / "native-transition-record.yaml" -EXPECTED_PARTICIPANTS = { - "task-b01", - "task-b02", - "task-b03", - "task-b03a", - "task-b04", - "task-b05", - "task-b01r", - "task-b04r", - "task-b05r", -} -EXPECTED_EXCLUDED_WORK = ["WOR-66", "WOR-79", "WOR-107", "work-bundle-mcp mutation"] -ALLOWED_TRANSITION_FIELDS = { - "schema", - "issue", - "transition_task", - "enforcement_transition", - "source_identity", - "review_path", - "review_sha256", - "accepted_commit", - "accepted_tree", - "release_anchor", - "integrated_validation", - "handoff_validation", - "participant_handoffs", - "native_scope", - "excluded_work", - "accepted_at", -} - - -def _yaml(path: Path) -> dict[str, object]: - value = yaml.safe_load(path.read_text(encoding="utf-8")) - assert isinstance(value, dict) - return value - - -def _validate_transition_record(transition: dict[str, object]) -> None: - assert set(transition) == ALLOWED_TRANSITION_FIELDS - assert transition["schema"] == "wor105-native-transition-v1" - assert transition["issue"] == "WOR-105" - assert transition["transition_task"] == "task-b06r" - assert transition["enforcement_transition"] == "bootstrap_policy_to_native" - assert transition["review_path"] == ( - ".work-bundle/orchestration/reviews/WOR-105-task-b06r-kernel-review-accepted.yaml" - ) - assert transition["native_scope"] == "subsequent WOR-105 phase-c through phase-f execution only" - assert transition["excluded_work"] == EXPECTED_EXCLUDED_WORK - assert re.fullmatch(r"[0-9a-f]{64}", str(transition["review_sha256"])) - assert re.fullmatch(r"[0-9a-f]{40}", str(transition["accepted_commit"])) - assert re.fullmatch(r"[0-9a-f]{40}", str(transition["accepted_tree"])) - release_anchor = transition["release_anchor"] - assert isinstance(release_anchor, dict) - assert release_anchor == { - "commit": "cfa089f0d2ed211b98d049eb37bfcdccb8091516", - "tree": "12e4a696c3caf991654f0b9ac9ef40594699c8d4", - } - assert re.fullmatch( - rf"{transition['accepted_commit']}\+repository-evidence-sha256:[0-9a-f]{{64}}", - str(transition["source_identity"]), - ) - assert re.fullmatch(r"\d{4}-\d{2}-\d{2}", str(transition["accepted_at"])) - - integrated = transition["integrated_validation"] - handoff = transition["handoff_validation"] - assert isinstance(integrated, dict) - assert isinstance(handoff, dict) - assert integrated["id"] == "VAL-B06R-TEST" - assert integrated["result"] == "passed" - assert isinstance(integrated["tests"], int) and integrated["tests"] > 0 - assert handoff["id"] == "VAL-B06R-IDENTITY" - assert handoff["result"] == "passed" - assert isinstance(handoff["adversarial_cases"], int) and handoff["adversarial_cases"] > 0 - - participants = transition["participant_handoffs"] - assert isinstance(participants, dict) - assert set(participants) == EXPECTED_PARTICIPANTS - assert all(re.fullmatch(r"[0-9a-f]{64}", str(digest)) for digest in participants.values()) - assert len(set(participants.values())) == len(EXPECTED_PARTICIPANTS) - - -def test_repository_frozen_transition_record_is_self_validating() -> None: - _validate_transition_record(_yaml(TRANSITION_RECORD)) - - -@pytest.mark.parametrize( - "mutation", - [ - lambda value: value.update(unrecognized_contract_field=True), - lambda value: value["participant_handoffs"].pop("task-b01"), - lambda value: value["participant_handoffs"].update({"task-b01": "not-a-digest"}), - lambda value: value.update(enforcement_transition="native_to_bootstrap_policy"), - lambda value: value.update(review_sha256="not-a-digest"), - lambda value: value["excluded_work"].append("unapproved-work"), - lambda value: value.update(source_identity="substitute-identity"), - lambda value: value["release_anchor"].update(commit="substitute-identity"), - ], -) -def test_frozen_transition_validation_rejects_incomplete_or_injected_records(mutation) -> None: - transition = deepcopy(_yaml(TRANSITION_RECORD)) - mutation(transition) - - with pytest.raises(AssertionError): - _validate_transition_record(transition) - - -def test_native_transition_binds_accepted_kernel_source_identity() -> None: - transition = _yaml(TRANSITION_RECORD) - accepted_commit = transition["accepted_commit"] - accepted_tree = subprocess.run( - ["git", "rev-parse", f"{accepted_commit}^{{tree}}"], - cwd=REPO_ROOT, - check=True, - capture_output=True, - text=True, - ).stdout.strip() - - assert transition["accepted_tree"] == accepted_tree - assert re.fullmatch( - rf"{accepted_commit}\+repository-evidence-sha256:[0-9a-f]{{64}}", - str(transition["source_identity"]), - ) diff --git a/tests/test_wor108_closure.py b/tests/test_wor108_closure.py deleted file mode 100644 index 8d9bc17..0000000 --- a/tests/test_wor108_closure.py +++ /dev/null @@ -1,108 +0,0 @@ -from __future__ import annotations - -from copy import deepcopy -import importlib.util -import json -from pathlib import Path - -import pytest - - -REPO_ROOT = Path(__file__).resolve().parents[1] -EVAL_ROOT = REPO_ROOT / "evals" / "wor108" -SPEC = importlib.util.spec_from_file_location("wor108_verify", EVAL_ROOT / "verify.py") -assert SPEC is not None and SPEC.loader is not None -wor108_verify = importlib.util.module_from_spec(SPEC) -SPEC.loader.exec_module(wor108_verify) - - -def test_wor108_closure_registry_has_exact_public_fixture_identities() -> None: - result = wor108_verify.verify() - assert result == { - "evaluation_id": "wor108-legacy-closure-v1", - "fixtures": 22, - "changed_surfaces": 63, - "verdict": "accepted", - } - - -def test_wor108_closure_registry_binds_linear_exact_production_oracles() -> None: - package = json.loads((EVAL_ROOT / "fixtures.json").read_text(encoding="utf-8")) - nodes_by_id = { - fixture["fixture_id"]: fixture["pytest_nodes"] - for fixture in package["fixtures"] - } - assert nodes_by_id | { - "RF-01": [ - "tests/test_wor108_review_frontier.py::test_rf_01_h1_repairs_recorded_a_without_reacquiring_unrecorded_latent_b" - ], - "RF-03": [ - "tests/test_wor108_review_frontier.py::test_rf_03_capable_untouched_invariant_stays_blocking_during_narrow_repair" - ], - "RF-06": [ - "tests/test_wor108_review_frontier.py::test_rf_06_final_broad_integrated_review_rediscovers_and_classifies_latent_b" - ], - "SG-04": [ - "tests/test_wor108_context_projection.py::test_completed_task_acceptance_rejects_controller_task_scope_mutation" - ], - } == nodes_by_id - - -def test_wor108_fixture_registry_rejects_missing_identity(tmp_path: Path) -> None: - fixtures = json.loads((EVAL_ROOT / "fixtures.json").read_text(encoding="utf-8")) - fixtures["fixtures"] = fixtures["fixtures"][:-1] - tampered = tmp_path / "fixtures.json" - tampered.write_text(json.dumps(fixtures), encoding="utf-8") - - with pytest.raises(wor108_verify.VerificationError, match="exactly RF-01..08"): - wor108_verify.verify(fixtures_path=tampered) - - -def test_wor108_fixture_schema_rejects_non_object_entries_and_numeric_ids( - tmp_path: Path, -) -> None: - schema = json.loads( - (EVAL_ROOT / "contracts-v1.schema.json").read_text(encoding="utf-8") - ) - fixture_schema = schema["$defs"]["fixture"] - assert fixture_schema["type"] == "object" - assert fixture_schema["properties"]["fixture_id"]["type"] == "string" - - fixtures = json.loads((EVAL_ROOT / "fixtures.json").read_text(encoding="utf-8")) - malformed_entry = deepcopy(fixtures) - malformed_entry["fixtures"][0] = 7 - malformed_entry_path = tmp_path / "malformed-entry.json" - malformed_entry_path.write_text(json.dumps(malformed_entry), encoding="utf-8") - with pytest.raises(wor108_verify.VerificationError, match="exactly RF-01..08"): - wor108_verify.verify(fixtures_path=malformed_entry_path) - - numeric_identity = deepcopy(fixtures) - numeric_identity["fixtures"][0]["fixture_id"] = 1 - numeric_identity_path = tmp_path / "numeric-identity.json" - numeric_identity_path.write_text(json.dumps(numeric_identity), encoding="utf-8") - with pytest.raises(wor108_verify.VerificationError, match="exactly RF-01..08"): - wor108_verify.verify(fixtures_path=numeric_identity_path) - - -def test_wor108_migration_impact_rejects_final_git_identity(tmp_path: Path) -> None: - manifest = json.loads((EVAL_ROOT / "migration-impact.json").read_text(encoding="utf-8")) - tampered_manifest = deepcopy(manifest) - tampered_manifest["final_tree"] = "0" * 40 - tampered = tmp_path / "migration-impact.json" - tampered.write_text(json.dumps(tampered_manifest), encoding="utf-8") - - with pytest.raises(wor108_verify.VerificationError, match="closed shape"): - wor108_verify.verify(migration_path=tampered) - - -def test_wor108_migration_impact_rejects_incomplete_baseline_delta( - tmp_path: Path, -) -> None: - manifest = json.loads((EVAL_ROOT / "migration-impact.json").read_text(encoding="utf-8")) - operations = manifest["changed_surfaces"]["operations"] - operations.remove("scripts/orchestration/execution_context.py") - tampered = tmp_path / "migration-impact.json" - tampered.write_text(json.dumps(manifest), encoding="utf-8") - - with pytest.raises(wor108_verify.VerificationError, match="changed surface completeness"): - wor108_verify.verify(migration_path=tampered) From e60adb254c70c0313ec08fa53a0434243f00910e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 15:24:31 +0800 Subject: [PATCH 32/48] fix(orchestration): admit historical cleanup targets --- scripts/orchestration/execution_context.py | 42 +++++++++++++++++----- tests/test_execution_artifact_placement.py | 32 +++++++++++++++++ 2 files changed, 65 insertions(+), 9 deletions(-) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index 2ee0bcc..6984ea0 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -15,7 +15,7 @@ from datetime import datetime, timezone -from core import is_relative_to, read_front_matter, resolve_workspace_root +from core import _member_roots, is_relative_to, read_front_matter, resolve_workspace_root from artifact_inputs import (_split_top_level, _split_key_value, _parse_scalar, parse_yaml_subset, _read_structured, _as_list, _input_path, _resolve_spec_paths) from repository_preflight import capture_repository_evidence, task_caused_paths @@ -4127,7 +4127,27 @@ def _assert_static_task_fields(task: dict[str, Any], task_path: Path) -> None: ) -def _assert_no_source_local_execution_artifacts(task: dict[str, Any], task_path: Path) -> None: +def _is_proven_historical_cleanup_target( + task: dict[str, Any], path: str, source_members: Iterable[Path] +) -> bool: + criteria = " ".join(str(value).lower() for value in _as_list(task.get("completion_criteria"))) + if "absent from source" not in criteria: + return False + for source_member in source_members: + history = subprocess.run( + ["git", "-C", str(source_member), "log", "--all", "-n", "1", "--format=%H", "--", path], + capture_output=True, + text=True, + check=False, + ) + if history.returncode == 0 and history.stdout.strip(): + return True + return False + + +def _assert_no_source_local_execution_artifacts( + task: dict[str, Any], task_path: Path, *, source_members: Iterable[Path] = () +) -> None: files = task.get("files") if isinstance(task.get("files"), dict) else {} write_paths = _as_list(files.get("write")) or _as_list(task.get("target_files")) for value in write_paths: @@ -4146,12 +4166,13 @@ def _assert_no_source_local_execution_artifacts(task: dict[str, Any], task_path: and parts[0] == "tests" and re.match(r"test_(?:wor|issue)[-_]?\d+(?:_|\.py)", parts[1], re.IGNORECASE) ) - if ( - path == "orchestration/executions" - or path.startswith("orchestration/executions/") - or issue_eval - or issue_test - ): + workspace_execution = path == "orchestration/executions" or path.startswith( + "orchestration/executions/" + ) + historical_cleanup = (issue_eval or issue_test) and _is_proven_historical_cleanup_target( + task, path, source_members + ) + if workspace_execution or ((issue_eval or issue_test) and not historical_cleanup): raise SystemExit( f"Task write scope uses a source-local execution artifact path: {task_path}: {path}" ) @@ -4162,7 +4183,10 @@ def static_task_brief(root: Path, task_path: Path) -> dict[str, Any]: task, _ = _read_structured(task_path) _assert_static_task_fields(task, task_path) - _assert_no_source_local_execution_artifacts(task, task_path) + source_members = _member_roots(root) if (root / ".work-bundle/project.yaml").is_file() else [] + if not source_members and (root / ".git").exists(): + source_members = [root] + _assert_no_source_local_execution_artifacts(task, task_path, source_members=source_members) compile_args = argparse.Namespace( project_root=str(root), workspace_root=str(root), diff --git a/tests/test_execution_artifact_placement.py b/tests/test_execution_artifact_placement.py index 9143349..2e36a1b 100644 --- a/tests/test_execution_artifact_placement.py +++ b/tests/test_execution_artifact_placement.py @@ -1,5 +1,6 @@ from __future__ import annotations +import subprocess import sys from pathlib import Path @@ -30,6 +31,37 @@ def test_static_admission_rejects_issue_run_artifacts_in_source(path: str) -> No execution_context._assert_no_source_local_execution_artifacts(task, Path("task.md")) +def test_static_admission_allows_only_proven_historical_cleanup_targets( + tmp_path: Path, +) -> None: + source = tmp_path / "source" + historical = source / "tests/test_wor108_context_projection.py" + historical.parent.mkdir(parents=True) + subprocess.run(["git", "init", "-q", "-b", "main"], cwd=source, check=True) + subprocess.run(["git", "config", "user.email", "test@example.com"], cwd=source, check=True) + subprocess.run(["git", "config", "user.name", "Test"], cwd=source, check=True) + historical.write_text("def test_historical(): pass\n", encoding="utf-8") + subprocess.run(["git", "add", "."], cwd=source, check=True) + subprocess.run(["git", "commit", "-qm", "historical execution test"], cwd=source, check=True) + historical.unlink() + subprocess.run(["git", "add", "-u"], cwd=source, check=True) + subprocess.run(["git", "commit", "-qm", "remove historical execution test"], cwd=source, check=True) + task = { + "target_files": ["tests/test_wor108_context_projection.py"], + "completion_criteria": ["Listed execution-only wrappers are absent from source."], + } + + execution_context._assert_no_source_local_execution_artifacts( + task, Path("task.md"), source_members=[source] + ) + + task["target_files"] = ["tests/test_wor999_new_evidence.py"] + with pytest.raises(SystemExit, match="source-local execution artifact"): + execution_context._assert_no_source_local_execution_artifacts( + task, Path("task.md"), source_members=[source] + ) + + def test_execution_artifacts_resolve_to_workspace_root_outside_source_member(tmp_path: Path) -> None: workspace = tmp_path / "workspace" source = workspace / "work-bundle-main" From f648dceb83acbe013727998406514f0bf31017c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 15:50:01 +0800 Subject: [PATCH 33/48] fix(orchestration): bind cleanup admission to baseline --- scripts/orchestration/execution_context.py | 51 ++++++++++++++++------ tests/test_execution_artifact_placement.py | 16 ++++++- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index 6984ea0..21a9472 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -15,7 +15,7 @@ from datetime import datetime, timezone -from core import _member_roots, is_relative_to, read_front_matter, resolve_workspace_root +from core import is_relative_to, read_front_matter, resolve_workspace_root from artifact_inputs import (_split_top_level, _split_key_value, _parse_scalar, parse_yaml_subset, _read_structured, _as_list, _input_path, _resolve_spec_paths) from repository_preflight import capture_repository_evidence, task_caused_paths @@ -4128,25 +4128,35 @@ def _assert_static_task_fields(task: dict[str, Any], task_path: Path) -> None: def _is_proven_historical_cleanup_target( - task: dict[str, Any], path: str, source_members: Iterable[Path] + task: dict[str, Any], path: str, cleanup_baselines: Mapping[Path, str] ) -> bool: + truth_basis = task.get("truth_basis") if isinstance(task.get("truth_basis"), dict) else {} + purpose = str(truth_basis.get("purpose") or "").lower() criteria = " ".join(str(value).lower() for value in _as_list(task.get("completion_criteria"))) - if "absent from source" not in criteria: + if "remove" not in purpose or "absent from source" not in criteria: return False - for source_member in source_members: - history = subprocess.run( - ["git", "-C", str(source_member), "log", "--all", "-n", "1", "--format=%H", "--", path], + for source_member, baseline in cleanup_baselines.items(): + at_baseline = subprocess.run( + ["git", "-C", str(source_member), "cat-file", "-e", f"{baseline}:{path}"], + capture_output=True, + text=True, + check=False, + ) + at_result = subprocess.run( + ["git", "-C", str(source_member), "cat-file", "-e", f"HEAD:{path}"], capture_output=True, text=True, check=False, ) - if history.returncode == 0 and history.stdout.strip(): + if at_baseline.returncode == 0 and at_result.returncode != 0 and not ( + source_member / path + ).exists(): return True return False def _assert_no_source_local_execution_artifacts( - task: dict[str, Any], task_path: Path, *, source_members: Iterable[Path] = () + task: dict[str, Any], task_path: Path, *, cleanup_baselines: Mapping[Path, str] | None = None ) -> None: files = task.get("files") if isinstance(task.get("files"), dict) else {} write_paths = _as_list(files.get("write")) or _as_list(task.get("target_files")) @@ -4170,7 +4180,7 @@ def _assert_no_source_local_execution_artifacts( "orchestration/executions/" ) historical_cleanup = (issue_eval or issue_test) and _is_proven_historical_cleanup_target( - task, path, source_members + task, path, cleanup_baselines or {} ) if workspace_execution or ((issue_eval or issue_test) and not historical_cleanup): raise SystemExit( @@ -4183,10 +4193,25 @@ def static_task_brief(root: Path, task_path: Path) -> dict[str, Any]: task, _ = _read_structured(task_path) _assert_static_task_fields(task, task_path) - source_members = _member_roots(root) if (root / ".work-bundle/project.yaml").is_file() else [] - if not source_members and (root / ".git").exists(): - source_members = [root] - _assert_no_source_local_execution_artifacts(task, task_path, source_members=source_members) + cleanup_baselines: dict[Path, str] = {} + task_plan_id = str(task.get("plan_id") or "") + task_id = str(task.get("id") or "") + binding_path = _binding_path(root, task_plan_id, task_id) + if binding_path.is_file(): + binding = load_task_execution_binding(root, task_plan_id, task_id) + baseline = binding.get("baseline") if isinstance(binding.get("baseline"), dict) else {} + execution_path = str(binding.get("execution_path") or "") + baseline_head = str(baseline.get("head") or "") + if ( + binding.get("plan_id") == task.get("plan_id") + and binding.get("task_id") == task.get("id") + and execution_path + and baseline_head + ): + cleanup_baselines[Path(execution_path).expanduser().resolve()] = baseline_head + _assert_no_source_local_execution_artifacts( + task, task_path, cleanup_baselines=cleanup_baselines + ) compile_args = argparse.Namespace( project_root=str(root), workspace_root=str(root), diff --git a/tests/test_execution_artifact_placement.py b/tests/test_execution_artifact_placement.py index 2e36a1b..73ce361 100644 --- a/tests/test_execution_artifact_placement.py +++ b/tests/test_execution_artifact_placement.py @@ -43,22 +43,34 @@ def test_static_admission_allows_only_proven_historical_cleanup_targets( historical.write_text("def test_historical(): pass\n", encoding="utf-8") subprocess.run(["git", "add", "."], cwd=source, check=True) subprocess.run(["git", "commit", "-qm", "historical execution test"], cwd=source, check=True) + accepted_baseline = subprocess.run( + ["git", "rev-parse", "HEAD"], cwd=source, check=True, capture_output=True, text=True + ).stdout.strip() historical.unlink() subprocess.run(["git", "add", "-u"], cwd=source, check=True) subprocess.run(["git", "commit", "-qm", "remove historical execution test"], cwd=source, check=True) task = { "target_files": ["tests/test_wor108_context_projection.py"], + "truth_basis": {"purpose": "Remove execution-only source residue."}, "completion_criteria": ["Listed execution-only wrappers are absent from source."], } execution_context._assert_no_source_local_execution_artifacts( - task, Path("task.md"), source_members=[source] + task, Path("task.md"), cleanup_baselines={source: accepted_baseline} ) + historical.write_text("def test_recreated(): pass\n", encoding="utf-8") + subprocess.run(["git", "add", "."], cwd=source, check=True) + subprocess.run(["git", "commit", "-qm", "recreate historical execution test"], cwd=source, check=True) + with pytest.raises(SystemExit, match="source-local execution artifact"): + execution_context._assert_no_source_local_execution_artifacts( + task, Path("task.md"), cleanup_baselines={source: accepted_baseline} + ) + task["target_files"] = ["tests/test_wor999_new_evidence.py"] with pytest.raises(SystemExit, match="source-local execution artifact"): execution_context._assert_no_source_local_execution_artifacts( - task, Path("task.md"), source_members=[source] + task, Path("task.md"), cleanup_baselines={source: accepted_baseline} ) From 2655baadccaff6d0c0afaa897af3e61615c43702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 16:10:44 +0800 Subject: [PATCH 34/48] docs(orchestration): align accepted lifecycle authority --- .../assets/orchestration/contract/plan-v1.md | 2 + references/assets/orchestration/workflow.md | 22 +++++++ rules/lifecycle-authority.md | 21 ++++-- .../orchestration/orch-artifact-authoring.md | 3 + .../orch-orchestration-boundary.md | 2 + rules/orchestration/orch-review-completion.md | 3 + rules/repository-boundary.md | 21 ++++-- rules/verification-evidence-before-claim.md | 2 + rules/work-bundle/wb-defect-evaluation.md | 3 + rules/work-bundle/wb-defect-evidence.md | 1 + .../orch-create-implementation-plan/SKILL.md | 2 + skills/orch-execute-plan/SKILL.md | 6 +- skills/orch-review-plan/SKILL.md | 1 + .../test_orchestration_skill_rule_boundary.py | 64 +++++++++++++++++++ .../test_orchestration_workflow_contracts.py | 20 ++++++ 15 files changed, 158 insertions(+), 15 deletions(-) diff --git a/references/assets/orchestration/contract/plan-v1.md b/references/assets/orchestration/contract/plan-v1.md index e4a7cb2..61eeb34 100644 --- a/references/assets/orchestration/contract/plan-v1.md +++ b/references/assets/orchestration/contract/plan-v1.md @@ -87,6 +87,8 @@ Plans do not optimize task or phase cardinality. Bound expected total orchestrat When execution proves a task materially under-decomposed, return to the plan and reslice only the affected region around the evidenced seam. Preserve the original binding, baseline, and accepted unaffected regions; do not repeatedly enlarge the task. +Plan review identity uses the canonical semantic plan projection shared by all lifecycle consumers; status-only or append-only evidence changes do not request review or reslicing, while authority, scope, dependency, acceptance, decomposition, or validation-allocation changes do. Before acceptance, canonical static task admission compiles every task and rejects missing dependencies, inconsistent authority, unsafe scope, and source-local execution artifacts. + Every executable task declares the same five-field Truth Basis. When a consequential simplification or compatibility assumption exists, make the earliest ordinary task cheaply falsify it before broad edits. Do not add a risk score, checkpoint phase, or parallel lifecycle. ## 4. Desired Files diff --git a/references/assets/orchestration/workflow.md b/references/assets/orchestration/workflow.md index 723219a..8fe2e01 100644 --- a/references/assets/orchestration/workflow.md +++ b/references/assets/orchestration/workflow.md @@ -170,6 +170,14 @@ remain single-flight. Publication rejects an intervening mutation epoch or expir freshness. Reservation lock files are retained to avoid splitting concurrent waiters; they are runtime artifacts, not source inputs or a separate cache subsystem. +The **acceptance once** lifecycle rule makes the harness strongly verify binding, +source/scope, subagent ownership, validation, and required review, then persists one +compact accepted result. Dependency release, finalization, resume, and archive consume +that result plus a current harness observation while its identity and freshness hold; +they do not replay transient acceptance evidence or historical handoff chains. A +status-only or append-only evidence change neither invalidates the canonical semantic +plan projection nor causes a terminal rerun. + Capability context projects trusted intent/evaluation seeds through the existing typed-relation traversal (`light`: 1 hop, `standard`: 2, `deep`: 4), bounded by `max_nodes`. Stale/non-authoritative nodes cannot be transit nodes; frontier and @@ -199,6 +207,13 @@ Selecting `orch-execute-plan` requires a subagent owner for every task without a On `repair`, return blocking findings with the same brief and current diff to the task-owning subagent. It makes the smallest repair, reruns claim-relevant validation, regenerates the package from the original base, and reviews again. If no subagent is available, fail closed before repair mutation. After two failed low-cost repair rounds, escalate the capability tier; if evidence indicates a plan or specification defect, stop the retry loop and route the typed blocker. +On reviewer infrastructure or provider failure, replace only the reviewer against the +same immutable review package; source identity, validation evidence, plan decomposition, +and review frontier remain unchanged. A finding-scoped repair under unchanged authority +carries the previous finding/evidence frontier and reviews only repaired boundaries. +Only a material authority, scope, acceptance, decomposition, or validation-allocation +change resets review to an initial frontier. + A task becomes `Completed` only when implementation criteria, fresh validation, a valid executor-result handoff, and a passing `validate-executor-result` check all exist. `Completed` does not require `verdict: accept` unless review was required. Phase and plan status derive from accepted children plus declared dependency and barrier gates. ## Failure routing @@ -217,6 +232,13 @@ workspace-blocked execution workspace preparation, hydration, ownership, or Resume the step that owns the failure. Repair a task for rejected implementation, a plan for decomposition defects, and a specification only for requirement, design, or authority defects. +Before responding to any evaluator, review, validation, or lifecycle failure, classify +the exact failing assertion into a causal class and route it to the first owning layer. +An evaluator expectation cannot create source authority. Historical validation uses an +exact baseline and endpoint rather than an open-ended live HEAD, and issue-run artifacts +remain in the workspace control plane; proven historical cleanup does not turn old +accepted manifests into a live source inventory. + ## Final workflow audit `orch-review-plan` audits workflow completion, required optional reviews, declared plan-level/integration acceptance, handoff integrity, knowledge disposition, finalization gates, and archive readiness. It checks declared completion evidence against the compiled Truth Basis, source IDs, expected delta, and remaining AUTH constraints. It does not redo task code review, reread implementation for code quality, or start another implementation-review agent. diff --git a/rules/lifecycle-authority.md b/rules/lifecycle-authority.md index 81a8be2..6d2dd02 100644 --- a/rules/lifecycle-authority.md +++ b/rules/lifecycle-authority.md @@ -11,22 +11,29 @@ requires: [] ## Purpose -- Define the enforceable contract for `rule-work-bundle-lifecycle-authority`. +Keep accepted execution authority compact and monotonic so later lifecycle consumers rely on the accepted result instead of replaying the evidence that originally established it. ## Must -- follow source authority -- keep runtime files compact +- Follow current source authority and exact execution bindings. +- Enforce acceptance once: strongly validate the bound task result, required review, ownership, repository identity, and harness observations before materializing one compact accepted result. +- Bind the compact accepted result to plan, task, source scope, subagent ownership, validation, review, commit, and tree identities. +- Make dependency release, phase/plan progression, finalization, resume, and archive consume the compact accepted result plus any current harness observation required by freshness policy. +- Invalidate acceptance only when a bound authority, source, scope, ownership, validation, review, commit/tree, or freshness identity materially changes. +- Keep runtime files compact. ## Must Not -- do not generate .mdc files -- do not include raw logs or secrets +- Do not generate `.mdc` files. +- Do not include raw logs or secrets. +- Do not replay transient acceptance evidence or historical handoff chains after the compact accepted result exists. +- Do not make status-only lifecycle progression or append-only evidence records create new plan authority, repeat acceptance, or rerun an otherwise current terminal validation. ## Validation -- required fields exist -- scope is work-bundle +- Required fields exist and the accepted result is bound to the current source and execution identities. +- Downstream consumers resolve the accepted result directly and reject stale or mismatched authority without reconstructing history. +- Scope is WorkBundle. ## On Violation diff --git a/rules/orchestration/orch-artifact-authoring.md b/rules/orchestration/orch-artifact-authoring.md index 6e1a5ca..46f31c4 100644 --- a/rules/orchestration/orch-artifact-authoring.md +++ b/rules/orchestration/orch-artifact-authoring.md @@ -28,6 +28,8 @@ Keep orchestration artifacts human-readable, contract-compliant, and executable - Assign every authoritative production path to a production owner. Reject helper-only allocation while a production path is unowned, and keep a coherent mechanical increment with one owner, oracle, and repair frontier together. - Create phases only for an actual barrier or convergence boundary. Reject speculative splits unsupported by current authority, repository, dependency, ownership, validation, or acceptance evidence. - When execution proves a task materially under-decomposed, return to the plan and reslice only the affected region while preserving the original binding, baseline, and accepted unaffected regions; do not repeatedly enlarge the task. +- Bind review freshness to one canonical semantic plan projection shared by all consumers. Status-only lifecycle fields and append-only evidence references do not change that projection; requirements, scope, dependencies, validation allocation, acceptance, or authority changes do. +- Run the compiler's canonical static task admission for every plan task before plan acceptance so missing dependencies, inconsistent authority, unsafe scope, and source-local execution artifacts fail before execution. - When plans contain contract-decoupled parallel tasks, include common contract groups, barrier participant maps, readiness criteria, release conditions, convergence owners, and task-level forbidden peer validation instructions. - Keep source-context, extra-evidence-loop, open-question, Knowledge Base Update, and body-level `Quality gate: verified|blocked` sections in specifications when required by the specification contract. - Summarize spec intent at most once in a root plan, then cite IDs for downstream detail. @@ -51,6 +53,7 @@ Contract loading by artifact type: - Repeat full requirement prose in plans, phases, or tasks when a spec-ID reference suffices. - Omit source files, target files, target symbols, validation rules, or completion criteria from executable tasks. - Use broad globs such as `src/**` as the only source or target path without exact files or narrow symbol-level explanation. +- Do not reslice a plan or request a fresh plan review for status-only or append-only evidence changes. - Split phases or tasks solely because of template habit, lifecycle labels, duplicated prose, a task-count target, or another cardinality preference when the coherent artifact remains complete and executable. - Encode sibling in-progress implementation files as dependencies for contract-decoupled parallel task validation; use common contracts, accepted prior handoffs, and post-barrier convergence instead. - Create phases or tasks whose target files are `.work-bundle/knowledge/**`. diff --git a/rules/orchestration/orch-orchestration-boundary.md b/rules/orchestration/orch-orchestration-boundary.md index c242689..0ea2dab 100644 --- a/rules/orchestration/orch-orchestration-boundary.md +++ b/rules/orchestration/orch-orchestration-boundary.md @@ -35,6 +35,7 @@ Orchestration artifacts are derived working material under `.work-bundle/orchest - Reference spec IDs in downstream plans, phases, and tasks instead of duplicating full requirement prose. - Carry only task-specific execution detail in task files after citing stable spec IDs. +- After acceptance, make downstream orchestration consume the compact accepted result and current harness observations; keep transient acceptance evidence and historical handoff chains out of dependency, finalization, resume, and archive context. ## Must Not @@ -44,6 +45,7 @@ Orchestration artifacts are derived working material under `.work-bundle/orchest - Duplicate full specifications inside plans or turn tasks into mini-specifications. - Embed implementation plans inside specifications or make phase or task files read like new specifications. - Perform orchestration artifact work from under the knowledge tree. +- Reconstruct accepted authority by replaying transient acceptance evidence or historical handoff chains after a compact accepted result is available. ## Validation diff --git a/rules/orchestration/orch-review-completion.md b/rules/orchestration/orch-review-completion.md index 36186eb..a741877 100644 --- a/rules/orchestration/orch-review-completion.md +++ b/rules/orchestration/orch-review-completion.md @@ -17,6 +17,8 @@ Keep final review focused on whether the WorkBundle workflow completed correctly ## Must - Confirm each required task review compared the accepted Truth Basis, implementation, test oracle, and task-local knowledge disposition before accepting the task. +- On reviewer infrastructure or provider failure, replace only the reviewer against the same immutable review package; preserve source identity, validation evidence, plan decomposition, and review frontier. +- For a finding-scoped repair review under unchanged authority, carry exactly the previous finding/evidence frontier and review only the repaired identity and affected boundaries. Reset to an initial frontier only after a material authority, scope, acceptance, decomposition, or validation-allocation change. - Check that declared completion evidence corresponds to the compiled Truth Basis, source IDs, expected delta, and remaining AUTH constraints. - Before archive or completion, confirm every accepted validation-bearing invariant has a compiled `evidence_capability` entry and capable, current, correctly bounded harness-observed evidence under its allocated INV/VAL identities. Treat incapable green, contradiction, staleness, wrong-boundary, failure, missing, or unexecuted evidence as negative acceptance evidence, not closure. - Use `no_validation_bearing_obligation + reason` only when no accepted validation-bearing obligation or design decision exists. Do not infer an empty evidence-capability map from a WOR-61 `none_relevant` impact result. @@ -51,6 +53,7 @@ Keep final review focused on whether the WorkBundle workflow completed correctly - Do not start another implementation-review agent for plan-level acceptance. - Do not repair implementation or test code during final review. - Do not substitute project-file inspection for accepted task-review evidence on tasks that explicitly required review. +- Do not replan, change source, rerun validation, or reconstruct review history solely because reviewer infrastructure or provider failure requires replacement. - Do not create a repair specification for every failed review gate. - Do not archive while required knowledge, validation, review, repository, or workspace evidence is unresolved. - Do not close an invariant on a green oracle that cannot observe it or that contradicts accepted authority. diff --git a/rules/repository-boundary.md b/rules/repository-boundary.md index cc7b094..9463209 100644 --- a/rules/repository-boundary.md +++ b/rules/repository-boundary.md @@ -11,22 +11,29 @@ requires: [] ## Purpose -- Define the enforceable contract for `rule-work-bundle-repository-boundary`. +Separate durable product source from workspace execution evidence and bind historical checks to immutable repository identities. ## Must -- follow source authority -- keep runtime files compact +- Follow source authority and resolve the selected repository before inspection, mutation, validation, or commit. +- Keep issue-run artifacts, execution manifests, task packets, and transient review evidence in the workspace control plane, never in product source. +- Treat historical cleanup as removal or generic promotion of proven legacy residue, not as authority to create new issue-named files or maintain a historical manifest as a live inventory. +- Bind historical validation to an exact baseline and endpoint commit/tree pair and require ancestry when the contract depends on a repository interval. +- Reject an open-ended live `HEAD` endpoint for reusable historical validation; current-head checks must state that current binding explicitly and remain non-reusable across source changes. +- Keep runtime files compact. ## Must Not -- do not generate .mdc files -- do not include raw logs or secrets +- Do not generate `.mdc` files. +- Do not include raw logs or secrets. +- Do not store issue-run artifacts under source `evals/`, source tests, or another product directory. +- Do not mutate historical accepted evidence merely because later repository files exist. ## Validation -- required fields exist -- scope is work-bundle +- Required fields exist and source/runtime placement follows the repository boundary. +- Historical checks name the exact baseline and endpoint; reusable checks contain no live `HEAD` endpoint. +- Source contains no newly created issue-run artifacts. ## On Violation diff --git a/rules/verification-evidence-before-claim.md b/rules/verification-evidence-before-claim.md index 67c72b6..a4d1fa0 100644 --- a/rules/verification-evidence-before-claim.md +++ b/rules/verification-evidence-before-claim.md @@ -18,6 +18,7 @@ Prevent completion language from outrunning the evidence available for the exact - Name the exact claim before selecting evidence. - Use capable evidence: the check must be able to disprove the claim, not merely inspect an adjacent property. - Obtain fresh, claim-relevant evidence after the latest material change. +- For a deterministic accepted validation identity, verify strongly once through the harness and persist the resulting compact observation. Later dependency, review, finalization, resume, and archive consumers reuse that current harness observation while its identity and freshness remain valid; lifecycle progression alone does not rerun it. - State only the status that evidence supports, including partial, failed, or blocked status. - For terminal, review, or archive claims, resolve the source artifact's `Knowledge Base Update` disposition to `completed` or `not-needed`, with supporting evidence. - Report the command, check, artifact, or observation that supports the claim. @@ -25,6 +26,7 @@ Prevent completion language from outrunning the evidence available for the exact ## Must Not - Do not reuse stale evidence after a relevant change. +- Do not replay executor assertions, transient acceptance evidence, or historical handoff chains in place of a current harness observation. - Do not extrapolate from partial evidence to a broader passing, clean, fixed, or complete claim. - Do not make a terminal or archive claim while required durable knowledge remains unresolved. - Do not treat absence of a visible error as proof of success. diff --git a/rules/work-bundle/wb-defect-evaluation.md b/rules/work-bundle/wb-defect-evaluation.md index 09fcafe..44802b5 100644 --- a/rules/work-bundle/wb-defect-evaluation.md +++ b/rules/work-bundle/wb-defect-evaluation.md @@ -22,6 +22,8 @@ Classify observed problems during WorkBundle-guided work early enough to preserv - Use the active workflow chain only as a bounded first-principles aid when visible evidence does not already establish WorkBundle relatedness. - Stop tracing as soon as visible evidence establishes that the problem is related to a WorkBundle toolkit artifact or workflow contract; do not continue searching for a deeper root cause before acting. - Classify the observed problem as exactly one of `work-bundle-scoped`, `project-scoped`, `mixed`, or `undetermined`. +- Determine the causal class before responding to a failed evaluator, review, validation, or lifecycle gate by reading the exact failing assertion and current authority, then route repair to the first owning layer: implementation, task/decomposition plan, specification/authority, evidence harness, knowledge, repository, or workspace. +- Preserve current accepted authority and review evidence when the causal class is observation-only or the failure belongs to infrastructure; a gate result or evaluator expectation cannot itself create a source requirement. - Use `work-bundle-scoped` when visible evidence shows a WorkBundle toolkit artifact or workflow contract caused or materially contributed to the problem. - Use `project-scoped` when the problem is limited to project business logic, project implementation, project data, or project-specific requirements with no visible WorkBundle process cause. - Use `mixed` when both WorkBundle toolkit behavior and project-specific behavior materially contribute to the problem. @@ -42,6 +44,7 @@ Classify observed problems during WorkBundle-guided work early enough to preserv - Do not create a new defect evidence file when evaluation returns `same-scope specification-owned` handling for exact current WorkBundle specification work. - Do not use `same-scope specification-owned` handling for unrelated WorkBundle defects, historical issues outside the active specification scope, or project-specific implementation defects. - Do not expand evaluation into unrelated repository browsing, historical reconstruction, durable knowledge retrieval, or broad contract exploration. +- Do not reslice a plan, reset review, rerun validation, or mutate historical evidence before the causal class identifies that layer as the first owning layer. - Do not store raw chat logs, private reasoning, or executor-result forbidden advice fields as the evaluation or evidence surface. - Do not silently continue when an `undetermined` classification affects authority, target scope, validation, or continuation. diff --git a/rules/work-bundle/wb-defect-evidence.md b/rules/work-bundle/wb-defect-evidence.md index 56ef1d6..61de41b 100644 --- a/rules/work-bundle/wb-defect-evidence.md +++ b/rules/work-bundle/wb-defect-evidence.md @@ -30,6 +30,7 @@ Keep the WorkBundle defect workflow visible whenever the Work Bundle rule is vis - Use `defect-migrate-store` explicitly when the legacy store remains; the other defect commands must fail before creating or reading the destination until migration completes. - Use the `defect-ensure-store`, `defect-create-evidence`, `defect-build-index`, `defect-write-index`, and `defect-archive-evidence` script entry points when writing, indexing, or moving evidence files is required. - Keep the evaluation compact and stop once visible WorkBundle relatedness is established. +- Record the causal class and first owning layer returned by `wb-defect-evaluation`; evidence preserves the observation but does not create implementation, plan, specification, or historical-evidence authority. - Treat project-scoped findings from `wb-defect-evaluation` as blockers reported to the user rather than work-bundle defect records. - Treat undetermined findings that affect authority, target scope, validation, or continuation as resolution blockers until evaluation can classify them. diff --git a/skills/orch-create-implementation-plan/SKILL.md b/skills/orch-create-implementation-plan/SKILL.md index aae1eb2..6efd527 100644 --- a/skills/orch-create-implementation-plan/SKILL.md +++ b/skills/orch-create-implementation-plan/SKILL.md @@ -19,6 +19,8 @@ Plan only from a verified active specification with converged semantics, resolve 6. Require a compact `executor-result-v1` handoff. Default `acceptance_review.required: false`. Require task review only when the task sets `acceptance_review.required: true`. Do not infer that flag from soft applicability prose. 7. When a consequential simplification or compatibility assumption exists, make the earliest ordinary task cheaply falsify it before broad edits. Do not add a risk score, checkpoint phase, or parallel lifecycle. 8. When execution proves a task materially under-decomposed, return to the plan and reslice only the affected region around the newly evidenced seam. Preserve the original binding, baseline, and accepted unaffected regions; do not repeatedly enlarge the task. +9. Use the canonical semantic plan projection for review identity and freshness; status-only or append-only evidence changes do not require plan review or reslicing, while authority, scope, dependency, acceptance, decomposition, or validation-allocation changes do. +10. Before plan acceptance, invoke canonical static task admission for every task through the task compiler. Do not duplicate its admission predicates in the planner. ## Methodology allocation diff --git a/skills/orch-execute-plan/SKILL.md b/skills/orch-execute-plan/SKILL.md index 876efa0..04bb26b 100644 --- a/skills/orch-execute-plan/SKILL.md +++ b/skills/orch-execute-plan/SKILL.md @@ -58,12 +58,16 @@ Use `--head worktree` for pre-commit review; the compiler includes tracked, stag The reviewer uses only the bounded package and `dev-code-review`. It compares the accepted Truth Basis, implementation, test oracle, and knowledge disposition, then returns `accept`, `repair`, or `blocked` with the reviewed tree identity and compact evidence-backed findings. -On `repair`, return blocking findings to the task-owning subagent with the same brief and current diff. That subagent makes the smallest repair, reruns fresh task validation, regenerates the package from the original task base, and re-reviews. If subagent execution becomes unavailable, fail closed before repair mutation. After two failed repair rounds, raise capability one tier when available. Repeated evidence of a decomposition or requirement defect stops retries and routes to plan or specification repair. +If reviewer infrastructure or provider failure prevents a verdict, replace only the reviewer against the same immutable review package. Preserve package/source identity, validation evidence, plan decomposition, and the existing frontier; do not change source, rerun validation, or reslice for reviewer availability. + +On `repair`, return blocking findings to the task-owning subagent with the same brief and current diff. That subagent makes the smallest repair, reruns fresh task validation, regenerates the package from the original task base, and re-reviews. A finding-scoped repair under unchanged authority carries exactly the previous finding/evidence frontier and limits review to the repaired identity and affected boundaries. Only a material authority, scope, acceptance, decomposition, or validation-allocation change resets review to an initial frontier. If subagent execution becomes unavailable, fail closed before repair mutation. After two failed repair rounds, raise capability one tier when available. Repeated evidence of a decomposition or requirement defect stops retries and routes to plan or specification repair. ## Completion semantics A task becomes `Completed` only when implementation criteria, fresh validation, neutral subagent ownership provenance, no controller mutation of task write scope, a valid executor-result handoff, and a passing `validate-executor-result` check all exist. `Completed` does not require `verdict: accept` unless review was required. Phase and plan completion derive from accepted children and declared dependency, barrier, and convergence gates. +Enforce acceptance once: after the helper verifies binding, source/scope, subagent ownership, validation, and required review, persist one compact accepted result. Dependency release and later lifecycle steps consume that result plus current harness observations; they do not replay transient acceptance evidence or historical handoff chains. + Use typed blockers: ```text diff --git a/skills/orch-review-plan/SKILL.md b/skills/orch-review-plan/SKILL.md index a15c23b..a3a1cca 100644 --- a/skills/orch-review-plan/SKILL.md +++ b/skills/orch-review-plan/SKILL.md @@ -29,6 +29,7 @@ Verify: - the resulting final Knowledge Base Update disposition is `completed` or `not-needed` before archive; - approved `ks-*` return evidence exists when durable knowledge was required; - allowed commit, applicable CodeGraph sync, metadata update, archive, and index refresh completed or are explicitly not applicable. +- dependency, finalization, resume, and archive decisions consume compact accepted results and current harness observations without replaying transient evidence or historical handoff chains. ## Evidence capability correspondence diff --git a/tests/test_orchestration_skill_rule_boundary.py b/tests/test_orchestration_skill_rule_boundary.py index dc802fa..d0c9535 100644 --- a/tests/test_orchestration_skill_rule_boundary.py +++ b/tests/test_orchestration_skill_rule_boundary.py @@ -256,6 +256,70 @@ def test_runtime_verification_classification_contract_routes_the_first_broken_ar assert "must not decide the semantic class" in text +def test_durable_owners_state_current_acceptance_and_review_semantics() -> None: + required = { + "rules/lifecycle-authority.md": [ + "compact accepted result", + "acceptance once", + "historical handoff chains", + "current harness observation", + ], + "rules/repository-boundary.md": [ + "issue-run artifacts", + "workspace control plane", + "exact baseline and endpoint", + "live `HEAD`", + "historical cleanup", + ], + "rules/work-bundle/wb-defect-evaluation.md": [ + "causal class", + "first owning layer", + "before responding", + ], + "rules/orchestration/orch-artifact-authoring.md": [ + "canonical semantic plan projection", + "static task admission", + "status-only", + ], + "rules/orchestration/orch-orchestration-boundary.md": [ + "compact accepted result", + "transient acceptance evidence", + "historical handoff chains", + ], + "rules/orchestration/orch-review-completion.md": [ + "reviewer infrastructure or provider failure", + "same immutable review package", + "finding-scoped repair review", + "previous finding/evidence frontier", + ], + "skills/orch-create-implementation-plan/SKILL.md": [ + "canonical semantic plan projection", + "static task admission", + "status-only or append-only evidence", + ], + "skills/orch-execute-plan/SKILL.md": [ + "compact accepted result", + "acceptance once", + "same immutable review package", + "previous finding/evidence frontier", + ], + "skills/orch-review-plan/SKILL.md": [ + "compact accepted results", + "historical handoff chains", + "current harness observations", + ], + "references/assets/orchestration/contract/plan-v1.md": [ + "canonical semantic plan projection", + "static task admission", + "status-only or append-only evidence", + ], + } + for relative, tokens in required.items(): + text = read(relative) + for token in tokens: + assert token in text, f"{relative}: {token}" + + def test_workflow_makes_task_review_optional_on_the_chain() -> None: text = read("references/assets/orchestration/workflow.md") for token in [ diff --git a/tests/test_orchestration_workflow_contracts.py b/tests/test_orchestration_workflow_contracts.py index c5bc146..f28e339 100644 --- a/tests/test_orchestration_workflow_contracts.py +++ b/tests/test_orchestration_workflow_contracts.py @@ -990,6 +990,26 @@ def test_workflow_assigns_review_ownership_and_repair_loop() -> None: assert token in workflow +def test_workflow_uses_accepted_results_without_lifecycle_replay() -> None: + workflow = read("references/assets/orchestration/workflow.md") + for token in [ + "acceptance once", + "compact accepted result", + "historical handoff chains", + "transient acceptance evidence", + "current harness observation", + "reviewer infrastructure or provider failure", + "same immutable review package", + "previous finding/evidence frontier", + "status-only or append-only evidence", + "causal class", + "first owning layer", + "exact baseline and endpoint", + "issue-run artifacts", + ]: + assert token in workflow + + def test_review_rule_uses_typed_resume_routing() -> None: rule = read("rules/orchestration/orch-review-completion.md") for token in [ From e8248df7514548be3ab07954c7573db5a571a806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 16:37:30 +0800 Subject: [PATCH 35/48] fix(orchestration): bound verification rule length --- rules/verification-evidence-before-claim.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rules/verification-evidence-before-claim.md b/rules/verification-evidence-before-claim.md index a4d1fa0..fbc03b6 100644 --- a/rules/verification-evidence-before-claim.md +++ b/rules/verification-evidence-before-claim.md @@ -11,32 +11,32 @@ requires: [] ## Purpose -Prevent completion language from outrunning the evidence available for the exact result being reported. +Keep completion claims evidence-bound. ## Must - Name the exact claim before selecting evidence. -- Use capable evidence: the check must be able to disprove the claim, not merely inspect an adjacent property. +- Use capable evidence that can disprove the claim. - Obtain fresh, claim-relevant evidence after the latest material change. -- For a deterministic accepted validation identity, verify strongly once through the harness and persist the resulting compact observation. Later dependency, review, finalization, resume, and archive consumers reuse that current harness observation while its identity and freshness remain valid; lifecycle progression alone does not rerun it. +- For a deterministic accepted identity, verify strongly once and persist the compact observation. Later lifecycle consumers reuse that current harness observation while its identity and freshness hold; progression alone does not rerun it. - State only the status that evidence supports, including partial, failed, or blocked status. -- For terminal, review, or archive claims, resolve the source artifact's `Knowledge Base Update` disposition to `completed` or `not-needed`, with supporting evidence. +- For terminal, review, or archive claims, resolve `Knowledge Base Update` to `completed` or `not-needed` with evidence. - Report the command, check, artifact, or observation that supports the claim. ## Must Not - Do not reuse stale evidence after a relevant change. - Do not replay executor assertions, transient acceptance evidence, or historical handoff chains in place of a current harness observation. -- Do not extrapolate from partial evidence to a broader passing, clean, fixed, or complete claim. +- Do not turn partial evidence into a broader passing, clean, fixed, or complete claim. - Do not make a terminal or archive claim while required durable knowledge remains unresolved. - Do not treat absence of a visible error as proof of success. ## Validation -- Match each completion claim to fresh evidence capable of testing it. +- Match each completion claim to capable, current evidence. - Confirm the reported status does not exceed the tested scope. -- Confirm terminal claims include resolved `Knowledge Base Update` evidence when applicable. +- Confirm applicable terminal claims include resolved `Knowledge Base Update` evidence. ## On Violation -Withdraw or narrow the unsupported claim, run the missing capable check, and report the supported status plus any blocker. +Withdraw or narrow the claim, run the missing capable check, and report only the supported status and blockers. From 418fa85866bef5ef6b2b0fa5b704a7de7479d0ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 17:17:17 +0800 Subject: [PATCH 36/48] fix(orchestration): admit prebinding cleanup plans --- scripts/orchestration/execution_context.py | 39 ++++++++++++++++++---- tests/test_execution_artifact_placement.py | 34 +++++++++++++++++++ 2 files changed, 66 insertions(+), 7 deletions(-) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index 21a9472..eced3bb 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -7,6 +7,7 @@ import hashlib import importlib.util import json +import os import re import subprocess import sys @@ -15,7 +16,7 @@ from datetime import datetime, timezone -from core import is_relative_to, read_front_matter, resolve_workspace_root +from core import _member_roots, is_relative_to, read_front_matter, resolve_workspace_root from artifact_inputs import (_split_top_level, _split_key_value, _parse_scalar, parse_yaml_subset, _read_structured, _as_list, _input_path, _resolve_spec_paths) from repository_preflight import capture_repository_evidence, task_caused_paths @@ -4128,13 +4129,27 @@ def _assert_static_task_fields(task: dict[str, Any], task_path: Path) -> None: def _is_proven_historical_cleanup_target( - task: dict[str, Any], path: str, cleanup_baselines: Mapping[Path, str] + task: dict[str, Any], + path: str, + cleanup_baselines: Mapping[Path, str], + planning_sources: Iterable[Path], ) -> bool: truth_basis = task.get("truth_basis") if isinstance(task.get("truth_basis"), dict) else {} purpose = str(truth_basis.get("purpose") or "").lower() criteria = " ".join(str(value).lower() for value in _as_list(task.get("completion_criteria"))) if "remove" not in purpose or "absent from source" not in criteria: return False + if not cleanup_baselines: + for source_member in planning_sources: + at_plan_head = subprocess.run( + ["git", "-C", str(source_member), "cat-file", "-e", f"HEAD:{path}"], + capture_output=True, + text=True, + check=False, + ) + if at_plan_head.returncode == 0 and os.path.lexists(source_member / path): + return True + return False for source_member, baseline in cleanup_baselines.items(): at_baseline = subprocess.run( ["git", "-C", str(source_member), "cat-file", "-e", f"{baseline}:{path}"], @@ -4148,15 +4163,19 @@ def _is_proven_historical_cleanup_target( text=True, check=False, ) - if at_baseline.returncode == 0 and at_result.returncode != 0 and not ( + if at_baseline.returncode == 0 and at_result.returncode != 0 and not os.path.lexists( source_member / path - ).exists(): + ): return True return False def _assert_no_source_local_execution_artifacts( - task: dict[str, Any], task_path: Path, *, cleanup_baselines: Mapping[Path, str] | None = None + task: dict[str, Any], + task_path: Path, + *, + cleanup_baselines: Mapping[Path, str] | None = None, + planning_sources: Iterable[Path] = (), ) -> None: files = task.get("files") if isinstance(task.get("files"), dict) else {} write_paths = _as_list(files.get("write")) or _as_list(task.get("target_files")) @@ -4180,7 +4199,7 @@ def _assert_no_source_local_execution_artifacts( "orchestration/executions/" ) historical_cleanup = (issue_eval or issue_test) and _is_proven_historical_cleanup_target( - task, path, cleanup_baselines or {} + task, path, cleanup_baselines or {}, planning_sources ) if workspace_execution or ((issue_eval or issue_test) and not historical_cleanup): raise SystemExit( @@ -4209,8 +4228,14 @@ def static_task_brief(root: Path, task_path: Path) -> dict[str, Any]: and baseline_head ): cleanup_baselines[Path(execution_path).expanduser().resolve()] = baseline_head + planning_sources = _member_roots(root) if (root / ".work-bundle/project.yaml").is_file() else [] + if not planning_sources and (root / ".git").exists(): + planning_sources = [root] _assert_no_source_local_execution_artifacts( - task, task_path, cleanup_baselines=cleanup_baselines + task, + task_path, + cleanup_baselines=cleanup_baselines, + planning_sources=planning_sources, ) compile_args = argparse.Namespace( project_root=str(root), diff --git a/tests/test_execution_artifact_placement.py b/tests/test_execution_artifact_placement.py index 73ce361..0b3290f 100644 --- a/tests/test_execution_artifact_placement.py +++ b/tests/test_execution_artifact_placement.py @@ -13,6 +13,7 @@ import execution_context # noqa: E402 from core import resolve_execution_artifact_path # noqa: E402 +from test_orchestration_execution_context import git, workspace # noqa: E402 @pytest.mark.parametrize( @@ -74,6 +75,39 @@ def test_static_admission_allows_only_proven_historical_cleanup_targets( ) +def test_static_task_admits_prebinding_cleanup_of_current_tracked_artifact( + tmp_path: Path, +) -> None: + root, _spec, task_path = workspace(tmp_path) + historical = root / "tests/test_wor108_context_projection.py" + historical.parent.mkdir(parents=True) + historical.write_text("def test_historical(): pass\n", encoding="utf-8") + task_path.write_text( + task_path.read_text(encoding="utf-8") + .replace( + "goal: Compile a bounded executor packet.\n", + "goal: Remove execution-only source residue.\n" + "completion_criteria: [Listed execution-only wrappers are absent from source.]\n", + ) + .replace( + "purpose: Compile a bounded executor packet.", + "purpose: Remove execution-only source residue.", + ) + .replace( + "write: [scripts/orchestration/execution_context.py]", + "write: [tests/test_wor108_context_projection.py]", + ), + encoding="utf-8", + ) + git(root, "add", ".") + git(root, "commit", "-qm", "tracked historical cleanup target") + + brief = execution_context.static_task_brief(root, task_path) + + assert brief["files"]["write"] == ["tests/test_wor108_context_projection.py"] + assert not (root / ".work-bundle/runtime").exists() + + def test_execution_artifacts_resolve_to_workspace_root_outside_source_member(tmp_path: Path) -> None: workspace = tmp_path / "workspace" source = workspace / "work-bundle-main" From 98b037b0607dffa848c115272456b9f6b87864b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 18:06:32 +0800 Subject: [PATCH 37/48] fix(orchestration): accept standalone task repair reviews --- scripts/orchestration/execution_context.py | 79 ++++++++++++ tests/test_orchestration_accepted_result.py | 136 ++++++++++++++++++++ 2 files changed, 215 insertions(+) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index eced3bb..ee9c71a 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -1777,6 +1777,85 @@ def load_current_accepted_task_result( return binding, dict(accepted) +def materialize_accepted_task_repair_review( + control_root: Path, + task: Mapping[str, Any], + review: Mapping[str, Any], + *, + accepted_at: str | None = None, +) -> dict[str, Any]: + """Advance compact accepted authority from one standalone task repair review.""" + + root = control_root.expanduser().resolve() + binding, prior = load_current_accepted_task_result(root, task) + if task.get("review_required") is not True: + raise SystemExit("accepted task repair review requires mandatory task review authority") + try: + from review_runtime import ReviewContractError, validate_task_acceptance_review + + validated_review = validate_task_acceptance_review(review) + except (ReviewContractError, KeyError, TypeError, ValueError) as error: + raise SystemExit(f"Accepted task repair review is invalid: {error}") from error + task_id = str(task.get("task_id") or "") + frontier = validated_review.repair_frontier + if ( + validated_review.review_mode != "repair" + or validated_review.verdict != "accepted" + or frontier is None + or validated_review.target_identity.get("artifact_id") != task_id + or frontier["previous_reviewed_identity"].get("artifact_id") != task_id + ): + raise SystemExit("accepted task repair review must bind the exact task and repair frontier") + reviewer = validated_review.reviewer + owner = prior.get("owner_identity") if isinstance(prior.get("owner_identity"), Mapping) else {} + if reviewer.get("agent_id") == owner.get("agent_id"): + raise SystemExit("accepted task repair review must be independent from the executor owner") + execution_path = Path(str(binding.get("execution_path") or "")).expanduser().resolve() + try: + evidence = capture_repository_evidence(execution_path) + except RuntimeError as error: + raise SystemExit("accepted task repair review Git identity is unavailable") from error + identity = validated_review.target_identity + if ( + evidence.get("status") != "clean" + or evidence.get("entries") + or review.get("reviewed_head") != identity.get("revision") + or evidence.get("head") != identity.get("revision") + or evidence.get("tree") != identity.get("source_tree") + ): + raise SystemExit("accepted task repair review does not match the clean exact source identity") + + authority_projection = dict(prior["authority_projection"]) + authority_projection["required_review_digest"] = semantic_digest( + _accepted_review_projection(review) + ) + accepted_source = {"head": evidence["head"], "tree": evidence["tree"]} + knowledge_disposition = prior.get("knowledge_disposition") + accepted_source["state_digest"] = _accepted_source_state_digest( + plan_id=str(prior["plan_id"]), + task_id=str(prior["task_id"]), + binding_id=str(prior["binding_id"]), + baseline_identity=prior["baseline_identity"], + head=accepted_source["head"], + tree=accepted_source["tree"], + authority_projection=authority_projection, + knowledge_disposition=( + knowledge_disposition if isinstance(knowledge_disposition, Mapping) else None + ), + ) + accepted = dict(prior) + accepted.update( + accepted_source=accepted_source, + authority_projection=authority_projection, + review_id=validated_review.review_id, + accepted_at=accepted_at or datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), + ) + updated = dict(binding) + updated["accepted_result"] = accepted + _persist_binding(updated, root) + return accepted + + def has_persisted_accepted_task_result( control_root: Path, plan_id: str, task_id: str ) -> bool: diff --git a/tests/test_orchestration_accepted_result.py b/tests/test_orchestration_accepted_result.py index c731b4a..3963fbb 100644 --- a/tests/test_orchestration_accepted_result.py +++ b/tests/test_orchestration_accepted_result.py @@ -15,6 +15,7 @@ sys.path.insert(0, str(ORCHESTRATION)) import execution_context # noqa: E402 +import review_runtime # noqa: E402 from task_ownership import ( # noqa: E402 OwnershipBlocker, TaskCandidate, @@ -245,6 +246,141 @@ def test_accepted_result_is_deterministic_current_authority_not_handoff_history( execution_context.assert_accepted_task_result_current(task, binding, invalidated) +def test_standalone_repair_review_rematerializes_compact_result_without_executor_replay( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + task = _task(tmp_path) + binding = _binding(tmp_path) + binding["baseline"] = {"head": OID_A, "tree": OID_B} + repository_evidence = {"head": OID_A, "tree": OID_B, "status": "clean", "entries": {}} + monkeypatch.setattr( + execution_context, "capture_repository_evidence", lambda _root: dict(repository_evidence) + ) + prior = execution_context.build_accepted_task_result( + task, + binding, + _handoff(), + _validated(), + accepted_at="2026-09-08T01:00:00Z", + ) + binding["accepted_result"] = prior + previous_identity = { + "artifact_id": "task-001", + "revision": OID_A, + "sha256": "1" * 64, + "source_tree": OID_B, + } + repaired_identity = { + "artifact_id": "task-001", + "revision": OID_C, + "sha256": "2" * 64, + "source_tree": OID_D, + } + reviewer = { + "agent_id": "reviewer-001", + "capability": "judgment", + "authorship": "none", + "repair_participation": "none", + "decision_participation": "none", + "deliberation_participation": "none", + "context_origin": "direct_source", + } + evidence = { + "mode": "direct", + "capabilities": ["bounded source inspection"], + "unavailable_evidence": [], + "commands": [], + "artifacts": [], + } + previous_review = { + "required": True, + "reviewer_independent": True, + "verdict": "repair", + "review_id": "review-finding-001", + "reviewed_head": OID_A, + "review_mode": "initial", + "review_target_kind": "task", + "repair_frontier": None, + "review_reset": None, + "target_identity": previous_identity, + "reviewer": reviewer, + "evidence": evidence, + "findings": [{ + "finding_id": "FINDING-001", + "stage": "implementation", + "class": "implementation_defect", + "severity": "blocking", + "first_broken_artifact": "implementation", + "obligation_basis": "accepted_requirement", + "evidence": [{ + "kind": "test", + "locator": "tests/test_orchestration_accepted_result.py", + "digest_or_identity": "red-001", + "observation": "standalone repair review could not be materialized", + }], + "target_identity": previous_identity, + "summary": "Repair acceptance was coupled to executor redispatch.", + "recommended_owner": "task_owner", + "disposition": "repair_task", + }], + "started_at": "2026-09-08T01:01:00Z", + "completed_at": "2026-09-08T01:02:00Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + repair_review = { + **previous_review, + "verdict": "accept", + "review_id": "review-repair-001", + "reviewed_head": OID_C, + "review_mode": "repair", + "target_identity": repaired_identity, + "findings": [], + "previous_review": previous_review, + "repair_frontier": { + "prior_review_id": "review-finding-001", + "blocking_finding_ids": ["FINDING-001"], + "previous_reviewed_identity": previous_identity, + "repaired_identity": repaired_identity, + "affected_boundaries": ["scripts/orchestration/execution_context.py"], + "frozen_evidence_reference": review_runtime.review_evidence_identity(previous_review), + }, + "started_at": "2026-09-08T01:03:00Z", + "completed_at": "2026-09-08T01:04:00Z", + } + persisted: dict[str, object] = {} + monkeypatch.setattr(execution_context, "load_task_execution_binding", lambda *_: binding) + repository_evidence.update(head=OID_C, tree=OID_D) + monkeypatch.setattr(execution_context, "_persist_binding", lambda value, _root: persisted.update(value)) + monkeypatch.setattr( + execution_context, + "build_accepted_task_result", + lambda *_args, **_kwargs: pytest.fail("executor handoff must not be replayed"), + ) + + repaired = execution_context.materialize_accepted_task_repair_review( + tmp_path, + task, + repair_review, + accepted_at="2026-09-08T01:05:00Z", + ) + + for field in ( + "baseline_identity", + "executor_result_digest", + "validation_evidence_ids", + "owner_identity", + "knowledge_disposition", + ): + assert repaired[field] == prior[field] + assert repaired["accepted_source"]["head"] == OID_C + assert repaired["accepted_source"]["tree"] == OID_D + assert repaired["review_id"] == "review-repair-001" + assert repaired["authority_projection"]["required_review_digest"] == execution_context.semantic_digest( + execution_context._accepted_review_projection(repair_review) + ) + assert persisted["accepted_result"] == repaired + assert "previous_review" not in repr(repaired) + def test_legacy_accepted_result_without_disposition_remains_current_for_nonknowledge_consumers( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: From fae720be4bc4df30665a574cb8e8c2079597e18c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 18:12:55 +0800 Subject: [PATCH 38/48] fix(orchestration): bind integrated repair findings --- scripts/orchestration/review_runtime.py | 9 ++++ tests/test_orchestration_reviews.py | 57 +++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index 3054191..03c1416 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -1329,6 +1329,15 @@ def validate_task_acceptance_review(value: Mapping[str, Any]) -> StageReviewV1: raise ReviewContractError("task repair review requires its exact previous_review") if "previous_review" in previous: raise ReviewContractError("task repair review may carry exactly one previous_review; older history stays lazy") + if previous.get("review_target_kind") == "stage": + validated_previous = validate_stage_review(previous) + if validated_previous.stage != "integrated_implementation": + raise ReviewContractError( + "task repair review stage predecessor must be integrated_implementation" + ) + return validate_review_sequence( + _task_review_as_stage(record), previous_review=previous + ) validate_task_review_record(previous) return validate_review_sequence(_task_review_as_stage(record), previous_review=_task_review_as_stage(previous)) if record.get("review_reset") is not None: diff --git a/tests/test_orchestration_reviews.py b/tests/test_orchestration_reviews.py index 5ab38d9..4754f8d 100644 --- a/tests/test_orchestration_reviews.py +++ b/tests/test_orchestration_reviews.py @@ -17,11 +17,13 @@ from review_runtime import ( # noqa: E402 ReviewContractError, classify_first_broken_owner, + review_evidence_identity, route_review_verdict, transition_review_finding, validate_contract_instance, validate_stage_review, validate_stage_reviews, + validate_task_acceptance_review, ) @@ -92,6 +94,61 @@ def stage_review(stage: str) -> dict[str, object]: } +def test_task_repair_review_binds_authoritative_integrated_stage_predecessor() -> None: + previous = stage_review("integrated_implementation") + previous.update( + review_id="review-integrated-finding", + review_mode="initial", + review_target_kind="stage", + repair_frontier=None, + review_reset=None, + verdict="repair", + ) + previous["target_identity"] = { + "artifact_id": "task-005", + "revision": "a" * 40, + "sha256": "1" * 64, + "source_tree": "b" * 40, + } + blocking = finding() + blocking["finding_id"] = "WOR112-T005-INT-001" + blocking["target_identity"] = previous["target_identity"] + previous["findings"] = [blocking] + repaired_identity = { + "artifact_id": "task-005", + "revision": "c" * 40, + "sha256": "2" * 64, + "source_tree": "d" * 40, + } + current = { + **stage_review("plan"), + "required": True, + "reviewer_independent": True, + "review_id": "review-task-repair", + "reviewed_head": repaired_identity["revision"], + "review_mode": "repair", + "review_target_kind": "task", + "repair_frontier": { + "prior_review_id": "review-integrated-finding", + "blocking_finding_ids": ["WOR112-T005-INT-001"], + "previous_reviewed_identity": previous["target_identity"], + "repaired_identity": repaired_identity, + "affected_boundaries": ["scripts/orchestration/review_runtime.py"], + "frozen_evidence_reference": review_evidence_identity(previous), + }, + "review_reset": None, + "target_identity": repaired_identity, + "verdict": "accept", + "findings": [], + "previous_review": previous, + } + + validated = validate_task_acceptance_review(current) + + assert validated.review_id == "review-task-repair" + assert validated.repair_frontier["prior_review_id"] == "review-integrated-finding" + + @pytest.mark.parametrize( ("finding_class", "expected"), [ From c3183695c7e9aa3b6e4dd5c443e4611215c240cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 18:18:23 +0800 Subject: [PATCH 39/48] fix(orchestration): pin repair acceptance endpoint --- scripts/orchestration/execution_context.py | 37 +++++++++-- tests/test_orchestration_accepted_result.py | 70 ++++++++++++++++----- 2 files changed, 89 insertions(+), 18 deletions(-) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index ee9c71a..5ef7de7 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -1816,20 +1816,49 @@ def materialize_accepted_task_repair_review( except RuntimeError as error: raise SystemExit("accepted task repair review Git identity is unavailable") from error identity = validated_review.target_identity + reviewed_head = str(identity.get("revision") or "") if ( evidence.get("status") != "clean" or evidence.get("entries") - or review.get("reviewed_head") != identity.get("revision") - or evidence.get("head") != identity.get("revision") - or evidence.get("tree") != identity.get("source_tree") + or review.get("reviewed_head") != reviewed_head ): raise SystemExit("accepted task repair review does not match the clean exact source identity") + if not re.fullmatch(r"[0-9a-f]{40}|[0-9a-f]{64}", reviewed_head): + raise SystemExit("accepted task repair review target revision is invalid") + reviewed_commit = subprocess.run( + ["git", "-C", str(execution_path), "rev-parse", "--verify", f"{reviewed_head}^{{commit}}"], + capture_output=True, + text=True, + ) + reviewed_tree = subprocess.run( + ["git", "-C", str(execution_path), "rev-parse", "--verify", f"{reviewed_head}^{{tree}}"], + capture_output=True, + text=True, + ) + if ( + reviewed_commit.returncode + or reviewed_tree.returncode + or reviewed_commit.stdout.strip() != reviewed_head + ): + raise SystemExit("accepted task repair review target revision does not resolve exactly") + if reviewed_tree.stdout.strip() != identity.get("source_tree"): + raise SystemExit("accepted task repair review target revision/tree identity is mismatched") + ancestor = subprocess.run( + [ + "git", "-C", str(execution_path), "merge-base", "--is-ancestor", + reviewed_head, str(evidence.get("head") or ""), + ], + capture_output=True, + text=True, + ) + if ancestor.returncode != 0: + raise SystemExit("accepted task repair review target is not an ancestor of current HEAD") authority_projection = dict(prior["authority_projection"]) authority_projection["required_review_digest"] = semantic_digest( _accepted_review_projection(review) ) - accepted_source = {"head": evidence["head"], "tree": evidence["tree"]} + accepted_source = {"head": reviewed_head, "tree": reviewed_tree.stdout.strip()} knowledge_disposition = prior.get("knowledge_disposition") accepted_source["state_digest"] = _accepted_source_state_digest( plan_id=str(prior["plan_id"]), diff --git a/tests/test_orchestration_accepted_result.py b/tests/test_orchestration_accepted_result.py index 3963fbb..3760d50 100644 --- a/tests/test_orchestration_accepted_result.py +++ b/tests/test_orchestration_accepted_result.py @@ -2,6 +2,7 @@ from copy import deepcopy from pathlib import Path +import subprocess import sys import pytest @@ -47,6 +48,13 @@ } +def _git(root: Path, *arguments: str) -> str: + completed = subprocess.run( + ["git", *arguments], cwd=root, check=True, capture_output=True, text=True + ) + return completed.stdout.strip() + + def _task(root: Path) -> dict[str, object]: return { "plan_id": "plan-001", @@ -249,13 +257,18 @@ def test_accepted_result_is_deterministic_current_authority_not_handoff_history( def test_standalone_repair_review_rematerializes_compact_result_without_executor_replay( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: + _git(tmp_path, "init", "-q") + _git(tmp_path, "config", "user.email", "test@example.com") + _git(tmp_path, "config", "user.name", "Test") + source = tmp_path / "source.py" + source.write_text("VALUE = 1\n") + _git(tmp_path, "add", "source.py") + _git(tmp_path, "commit", "-qm", "accepted executor result") + accepted_head = _git(tmp_path, "rev-parse", "HEAD") + accepted_tree = _git(tmp_path, "rev-parse", "HEAD^{tree}") task = _task(tmp_path) binding = _binding(tmp_path) - binding["baseline"] = {"head": OID_A, "tree": OID_B} - repository_evidence = {"head": OID_A, "tree": OID_B, "status": "clean", "entries": {}} - monkeypatch.setattr( - execution_context, "capture_repository_evidence", lambda _root: dict(repository_evidence) - ) + binding["baseline"] = {"head": accepted_head, "tree": accepted_tree} prior = execution_context.build_accepted_task_result( task, binding, @@ -266,15 +279,20 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor binding["accepted_result"] = prior previous_identity = { "artifact_id": "task-001", - "revision": OID_A, + "revision": accepted_head, "sha256": "1" * 64, - "source_tree": OID_B, + "source_tree": accepted_tree, } + source.write_text("VALUE = 2\n") + _git(tmp_path, "add", "source.py") + _git(tmp_path, "commit", "-qm", "repair reviewed endpoint") + reviewed_head = _git(tmp_path, "rev-parse", "HEAD") + reviewed_tree = _git(tmp_path, "rev-parse", "HEAD^{tree}") repaired_identity = { "artifact_id": "task-001", - "revision": OID_C, + "revision": reviewed_head, "sha256": "2" * 64, - "source_tree": OID_D, + "source_tree": reviewed_tree, } reviewer = { "agent_id": "reviewer-001", @@ -297,7 +315,7 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor "reviewer_independent": True, "verdict": "repair", "review_id": "review-finding-001", - "reviewed_head": OID_A, + "reviewed_head": accepted_head, "review_mode": "initial", "review_target_kind": "task", "repair_frontier": None, @@ -331,7 +349,7 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor **previous_review, "verdict": "accept", "review_id": "review-repair-001", - "reviewed_head": OID_C, + "reviewed_head": reviewed_head, "review_mode": "repair", "target_identity": repaired_identity, "findings": [], @@ -347,9 +365,11 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor "started_at": "2026-09-08T01:03:00Z", "completed_at": "2026-09-08T01:04:00Z", } + (tmp_path / "unrelated.py").write_text("UNCHANGED_FRONTIER = True\n") + _git(tmp_path, "add", "unrelated.py") + _git(tmp_path, "commit", "-qm", "later unrelated lifecycle progress") persisted: dict[str, object] = {} monkeypatch.setattr(execution_context, "load_task_execution_binding", lambda *_: binding) - repository_evidence.update(head=OID_C, tree=OID_D) monkeypatch.setattr(execution_context, "_persist_binding", lambda value, _root: persisted.update(value)) monkeypatch.setattr( execution_context, @@ -372,8 +392,8 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor "knowledge_disposition", ): assert repaired[field] == prior[field] - assert repaired["accepted_source"]["head"] == OID_C - assert repaired["accepted_source"]["tree"] == OID_D + assert repaired["accepted_source"]["head"] == reviewed_head + assert repaired["accepted_source"]["tree"] == reviewed_tree assert repaired["review_id"] == "review-repair-001" assert repaired["authority_projection"]["required_review_digest"] == execution_context.semantic_digest( execution_context._accepted_review_projection(repair_review) @@ -381,6 +401,28 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor assert persisted["accepted_result"] == repaired assert "previous_review" not in repr(repaired) + divergent_head = _git(tmp_path, "commit-tree", accepted_tree, "-m", "divergent endpoint") + cases = [ + (reviewed_head, accepted_tree, "revision/tree identity is mismatched"), + ("f" * 40, accepted_tree, "target revision does not resolve"), + (divergent_head, accepted_tree, "not an ancestor"), + ] + for target_head, target_tree, message in cases: + invalid = deepcopy(repair_review) + invalid_identity = { + **invalid["target_identity"], + "revision": target_head, + "source_tree": target_tree, + } + invalid["reviewed_head"] = target_head + invalid["target_identity"] = invalid_identity + invalid["repair_frontier"]["repaired_identity"] = invalid_identity + with pytest.raises(SystemExit, match=message): + execution_context.materialize_accepted_task_repair_review( + tmp_path, task, invalid, accepted_at="2026-09-08T01:05:00Z" + ) + + def test_legacy_accepted_result_without_disposition_remains_current_for_nonknowledge_consumers( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: From 3f0c6171e4cc28a5b032ec1ba7d1362204e61363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 18:22:32 +0800 Subject: [PATCH 40/48] fix(orchestration): bind repair predecessor owner --- scripts/orchestration/execution_context.py | 22 +++++++++++- tests/test_orchestration_accepted_result.py | 38 ++++++++++++++++++--- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index 5ef7de7..9a83732 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -1797,13 +1797,33 @@ def materialize_accepted_task_repair_review( except (ReviewContractError, KeyError, TypeError, ValueError) as error: raise SystemExit(f"Accepted task repair review is invalid: {error}") from error task_id = str(task.get("task_id") or "") + plan_id = str(task.get("plan_id") or "") frontier = validated_review.repair_frontier + previous_review = review.get("previous_review") + previous_kind = ( + previous_review.get("review_target_kind") + if isinstance(previous_review, Mapping) + else None + ) + previous_artifact = ( + frontier["previous_reviewed_identity"].get("artifact_id") + if frontier is not None + else None + ) + previous_owner_matches = ( + previous_kind == "task" and previous_artifact == task_id + ) or ( + previous_kind == "stage" + and previous_review.get("stage") == "integrated_implementation" + and previous_artifact == plan_id + ) if ( validated_review.review_mode != "repair" or validated_review.verdict != "accepted" or frontier is None or validated_review.target_identity.get("artifact_id") != task_id - or frontier["previous_reviewed_identity"].get("artifact_id") != task_id + or frontier["repaired_identity"].get("artifact_id") != task_id + or not previous_owner_matches ): raise SystemExit("accepted task repair review must bind the exact task and repair frontier") reviewer = validated_review.reviewer diff --git a/tests/test_orchestration_accepted_result.py b/tests/test_orchestration_accepted_result.py index 3760d50..c271223 100644 --- a/tests/test_orchestration_accepted_result.py +++ b/tests/test_orchestration_accepted_result.py @@ -254,8 +254,9 @@ def test_accepted_result_is_deterministic_current_authority_not_handoff_history( execution_context.assert_accepted_task_result_current(task, binding, invalidated) +@pytest.mark.parametrize("predecessor_kind", ["task", "integrated_stage"]) def test_standalone_repair_review_rematerializes_compact_result_without_executor_replay( - tmp_path: Path, monkeypatch: pytest.MonkeyPatch + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, predecessor_kind: str ) -> None: _git(tmp_path, "init", "-q") _git(tmp_path, "config", "user.email", "test@example.com") @@ -278,7 +279,7 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor ) binding["accepted_result"] = prior previous_identity = { - "artifact_id": "task-001", + "artifact_id": "task-001" if predecessor_kind == "task" else "plan-001", "revision": accepted_head, "sha256": "1" * 64, "source_tree": accepted_tree, @@ -310,9 +311,7 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor "commands": [], "artifacts": [], } - previous_review = { - "required": True, - "reviewer_independent": True, + previous_review: dict[str, object] = { "verdict": "repair", "review_id": "review-finding-001", "reviewed_head": accepted_head, @@ -345,8 +344,21 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor "completed_at": "2026-09-08T01:02:00Z", "staleness": {"is_stale": False, "reason": None, "supersedes": None}, } + if predecessor_kind == "task": + previous_review.update( + required=True, + reviewer_independent=True, + reviewed_head=accepted_head, + ) + else: + previous_review.pop("reviewed_head") + previous_review["review_target_kind"] = "stage" + previous_review["stage"] = "integrated_implementation" repair_review = { **previous_review, + "required": True, + "reviewer_independent": True, + "review_target_kind": "task", "verdict": "accept", "review_id": "review-repair-001", "reviewed_head": reviewed_head, @@ -421,6 +433,22 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor execution_context.materialize_accepted_task_repair_review( tmp_path, task, invalid, accepted_at="2026-09-08T01:05:00Z" ) + if predecessor_kind == "integrated_stage": + wrong_plan = deepcopy(repair_review) + wrong_identity = { + **wrong_plan["previous_review"]["target_identity"], + "artifact_id": "plan-other", + } + wrong_plan["previous_review"]["target_identity"] = wrong_identity + wrong_plan["previous_review"]["findings"][0]["target_identity"] = wrong_identity + wrong_plan["repair_frontier"]["previous_reviewed_identity"] = wrong_identity + with pytest.raises(SystemExit, match="exact task and repair frontier"): + execution_context.materialize_accepted_task_repair_review(tmp_path, task, wrong_plan) + + wrong_stage = deepcopy(repair_review) + wrong_stage["previous_review"]["stage"] = "plan" + with pytest.raises(SystemExit, match="stage predecessor must be integrated_implementation"): + execution_context.materialize_accepted_task_repair_review(tmp_path, task, wrong_stage) def test_legacy_accepted_result_without_disposition_remains_current_for_nonknowledge_consumers( From e95e69fa0fd83ebeef6928adc305b8fb2ab226aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 18:29:38 +0800 Subject: [PATCH 41/48] fix(orchestration): scope integrated repair findings --- scripts/orchestration/review_runtime.py | 25 ++++++++++++++++++++++--- tests/test_orchestration_reviews.py | 15 ++++++++++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index 03c1416..81b9a86 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -1255,6 +1255,7 @@ def validate_stage_review( def validate_review_sequence( value: Mapping[str, Any], *, previous_review: Mapping[str, Any] | None = None, material_change: str | None = None, + _task_owned_finding_subset: bool = False, ) -> StageReviewV1: """Bind a re-review to its exact predecessor without replaying history.""" current = validate_stage_review(value) @@ -1273,8 +1274,24 @@ def validate_review_sequence( raise ReviewContractError("repair frontier must bind the exact prior repair review") if frontier["previous_reviewed_identity"] != previous.target_identity: raise ReviewContractError("repair frontier previous reviewed identity does not match prior review") - expected_findings = {item.finding_id for item in previous.findings if item.severity == "blocking"} - if set(frontier["blocking_finding_ids"]) != expected_findings: + blocking_findings = { + item.finding_id: item for item in previous.findings if item.severity == "blocking" + } + selected_findings = set(frontier["blocking_finding_ids"]) + if _task_owned_finding_subset: + if not selected_findings.issubset(blocking_findings): + raise ReviewContractError( + "task repair frontier contains unknown blocking finding IDs" + ) + if any( + blocking_findings[finding_id].recommended_owner != "task_owner" + or blocking_findings[finding_id].disposition != "repair_task" + for finding_id in selected_findings + ): + raise ReviewContractError( + "task repair frontier may select only task-owned blocking findings" + ) + elif selected_findings != set(blocking_findings): raise ReviewContractError("repair frontier blocking finding IDs do not match prior review") if frontier["frozen_evidence_reference"] != review_evidence_identity(previous_review): raise ReviewContractError("repair frontier frozen evidence reference does not match prior review") @@ -1336,7 +1353,9 @@ def validate_task_acceptance_review(value: Mapping[str, Any]) -> StageReviewV1: "task repair review stage predecessor must be integrated_implementation" ) return validate_review_sequence( - _task_review_as_stage(record), previous_review=previous + _task_review_as_stage(record), + previous_review=previous, + _task_owned_finding_subset=True, ) validate_task_review_record(previous) return validate_review_sequence(_task_review_as_stage(record), previous_review=_task_review_as_stage(previous)) diff --git a/tests/test_orchestration_reviews.py b/tests/test_orchestration_reviews.py index 4754f8d..db14976 100644 --- a/tests/test_orchestration_reviews.py +++ b/tests/test_orchestration_reviews.py @@ -113,7 +113,10 @@ def test_task_repair_review_binds_authoritative_integrated_stage_predecessor() - blocking = finding() blocking["finding_id"] = "WOR112-T005-INT-001" blocking["target_identity"] = previous["target_identity"] - previous["findings"] = [blocking] + evaluator_control = finding("validation_oracle_defect") + evaluator_control["finding_id"] = "WOR112-EVALUATOR-CONTROL-001" + evaluator_control["target_identity"] = previous["target_identity"] + previous["findings"] = [blocking, evaluator_control] repaired_identity = { "artifact_id": "task-005", "revision": "c" * 40, @@ -148,6 +151,16 @@ def test_task_repair_review_binds_authoritative_integrated_stage_predecessor() - assert validated.review_id == "review-task-repair" assert validated.repair_frontier["prior_review_id"] == "review-integrated-finding" + for finding_ids, message in ( + (["UNKNOWN-FINDING"], "unknown blocking finding IDs"), + (["WOR112-EVALUATOR-CONTROL-001"], "only task-owned blocking findings"), + ([], "must be non-empty"), + ): + invalid = deepcopy(current) + invalid["repair_frontier"]["blocking_finding_ids"] = finding_ids + with pytest.raises(ReviewContractError, match=message): + validate_task_acceptance_review(invalid) + @pytest.mark.parametrize( ("finding_class", "expected"), From 3d1fe79ae38903e9279506a01c90587d0765f209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 18:43:58 +0800 Subject: [PATCH 42/48] fix(orchestration): compose current accepted authority --- scripts/orchestration/execution_context.py | 119 +++++++++++++++++--- tests/test_orchestration_accepted_result.py | 108 +++++++++++++++++- 2 files changed, 211 insertions(+), 16 deletions(-) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index 9a83732..dcbc866 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -1777,17 +1777,77 @@ def load_current_accepted_task_result( return binding, dict(accepted) -def materialize_accepted_task_repair_review( +def _load_materialized_accepted_task_result( + root: Path, task: Mapping[str, Any] +) -> tuple[dict[str, Any], dict[str, Any]]: + """Load and authenticate compact acceptance without requiring old authority to be current.""" + + binding = load_task_execution_binding( + root, str(task.get("plan_id") or ""), str(task.get("task_id") or "") + ) + prior = binding.get("accepted_result") + if not isinstance(prior, Mapping): + raise SystemExit("accepted task result is missing") + fields = frozenset(prior) + if prior.get("schema") != ACCEPTED_TASK_RESULT_SCHEMA or fields not in { + frozenset(LEGACY_ACCEPTED_TASK_RESULT_FIELDS), frozenset(ACCEPTED_TASK_RESULT_FIELDS) + }: + raise SystemExit("materialized accepted task result shape is invalid") + ownership = binding.get("ownership") if isinstance(binding.get("ownership"), Mapping) else {} + source = prior.get("accepted_source") + projection = prior.get("authority_projection") + disposition = prior.get("knowledge_disposition") + if ( + prior.get("plan_id") != str(task.get("plan_id") or "") + or prior.get("task_id") != str(task.get("task_id") or "") + or prior.get("binding_id") != ownership.get("binding_id") + or prior.get("baseline_identity") != _accepted_baseline_identity(binding) + or prior.get("invalidation") is not None + or not isinstance(source, Mapping) + or set(source) != {"head", "tree", "state_digest"} + or not isinstance(projection, Mapping) + or set(projection) != ACCEPTED_AUTHORITY_PROJECTION_FIELDS + or not isinstance(prior.get("owner_identity"), Mapping) + or ("knowledge_disposition" in prior and not isinstance(disposition, Mapping)) + ): + raise SystemExit("materialized accepted task result identity is invalid") + expected_state = _accepted_source_state_digest( + plan_id=str(prior["plan_id"]), task_id=str(prior["task_id"]), + binding_id=str(prior["binding_id"]), baseline_identity=prior["baseline_identity"], + head=source.get("head"), tree=source.get("tree"), authority_projection=projection, + knowledge_disposition=disposition if isinstance(disposition, Mapping) else None, + ) + if source.get("state_digest") != expected_state: + raise SystemExit("materialized accepted task result compact authority is invalid") + return binding, dict(prior) + + +def materialize_accepted_task_review( control_root: Path, task: Mapping[str, Any], review: Mapping[str, Any], + causal_classification: Mapping[str, Any], *, accepted_at: str | None = None, ) -> dict[str, Any]: - """Advance compact accepted authority from one standalone task repair review.""" + """Compose prior executor authority with one standalone current review.""" root = control_root.expanduser().resolve() - binding, prior = load_current_accepted_task_result(root, task) + expected_classification = { + "causal_class", "affected_task", "authorized_lifecycle_action", + } + if ( + not isinstance(causal_classification, Mapping) + or set(causal_classification) != expected_classification + or causal_classification.get("causal_class") not in { + "claim_relevant_drift", "implementation_defect", + } + or causal_classification.get("affected_task") != str(task.get("task_id") or "") + or causal_classification.get("authorized_lifecycle_action") + != "rematerialize_accepted_result" + ): + raise SystemExit("accepted task review requires an exact controller causal classification") + binding, prior = _load_materialized_accepted_task_result(root, task) if task.get("review_required") is not True: raise SystemExit("accepted task repair review requires mandatory task review authority") try: @@ -1799,17 +1859,19 @@ def materialize_accepted_task_repair_review( task_id = str(task.get("task_id") or "") plan_id = str(task.get("plan_id") or "") frontier = validated_review.repair_frontier + reset = validated_review.review_reset previous_review = review.get("previous_review") previous_kind = ( previous_review.get("review_target_kind") if isinstance(previous_review, Mapping) else None ) - previous_artifact = ( - frontier["previous_reviewed_identity"].get("artifact_id") - if frontier is not None - else None + previous_identity = ( + frontier["previous_reviewed_identity"] if frontier is not None + else previous_review.get("target_identity") if isinstance(previous_review, Mapping) + else {} ) + previous_artifact = previous_identity.get("artifact_id") previous_owner_matches = ( previous_kind == "task" and previous_artifact == task_id ) or ( @@ -1818,14 +1880,20 @@ def materialize_accepted_task_repair_review( and previous_artifact == plan_id ) if ( - validated_review.review_mode != "repair" - or validated_review.verdict != "accepted" - or frontier is None + validated_review.verdict != "accepted" or validated_review.target_identity.get("artifact_id") != task_id - or frontier["repaired_identity"].get("artifact_id") != task_id or not previous_owner_matches ): - raise SystemExit("accepted task repair review must bind the exact task and repair frontier") + raise SystemExit("accepted task review must bind the exact current task and predecessor owner") + if validated_review.review_mode == "repair": + if frontier is None or frontier["repaired_identity"].get("artifact_id") != task_id: + raise SystemExit("accepted task repair review must bind the exact repair frontier") + elif ( + reset is None + or reset.get("reason_class") not in {"scope", "validation_allocation"} + or reset.get("prior_review_id") != prior.get("review_id") + ): + raise SystemExit("accepted task initial review must reset exact prior scope or validation authority") reviewer = validated_review.reviewer owner = prior.get("owner_identity") if isinstance(prior.get("owner_identity"), Mapping) else {} if reviewer.get("agent_id") == owner.get("agent_id"): @@ -1874,9 +1942,8 @@ def materialize_accepted_task_repair_review( if ancestor.returncode != 0: raise SystemExit("accepted task repair review target is not an ancestor of current HEAD") - authority_projection = dict(prior["authority_projection"]) - authority_projection["required_review_digest"] = semantic_digest( - _accepted_review_projection(review) + authority_projection = _accepted_authority_projection( + task, binding, accepted_review=review, owner_identity=prior["owner_identity"] ) accepted_source = {"head": reviewed_head, "tree": reviewed_tree.stdout.strip()} knowledge_disposition = prior.get("knowledge_disposition") @@ -1905,6 +1972,28 @@ def materialize_accepted_task_repair_review( return accepted +def materialize_accepted_task_repair_review( + control_root: Path, + task: Mapping[str, Any], + review: Mapping[str, Any], + *, + accepted_at: str | None = None, +) -> dict[str, Any]: + """Compatibility wrapper for unchanged-authority standalone task repair review.""" + + return materialize_accepted_task_review( + control_root, + task, + review, + { + "causal_class": "implementation_defect", + "affected_task": str(task.get("task_id") or ""), + "authorized_lifecycle_action": "rematerialize_accepted_result", + }, + accepted_at=accepted_at, + ) + + def has_persisted_accepted_task_result( control_root: Path, plan_id: str, task_id: str ) -> bool: diff --git a/tests/test_orchestration_accepted_result.py b/tests/test_orchestration_accepted_result.py index c271223..c0ca624 100644 --- a/tests/test_orchestration_accepted_result.py +++ b/tests/test_orchestration_accepted_result.py @@ -4,6 +4,7 @@ from pathlib import Path import subprocess import sys +from types import SimpleNamespace import pytest @@ -442,7 +443,7 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor wrong_plan["previous_review"]["target_identity"] = wrong_identity wrong_plan["previous_review"]["findings"][0]["target_identity"] = wrong_identity wrong_plan["repair_frontier"]["previous_reviewed_identity"] = wrong_identity - with pytest.raises(SystemExit, match="exact task and repair frontier"): + with pytest.raises(SystemExit, match="exact current task and predecessor owner"): execution_context.materialize_accepted_task_repair_review(tmp_path, task, wrong_plan) wrong_stage = deepcopy(repair_review) @@ -451,6 +452,111 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor execution_context.materialize_accepted_task_repair_review(tmp_path, task, wrong_stage) +def test_standalone_review_recomposes_changed_task_authority_without_executor_replay( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + old_task = _task(tmp_path) + binding = _binding(tmp_path) + monkeypatch.setattr( + execution_context, + "capture_repository_evidence", + lambda _root: {"head": OID_A, "tree": OID_B, "status": "clean", "entries": {}}, + ) + prior = execution_context.build_accepted_task_result( + old_task, binding, _handoff(), _validated(), accepted_at="2026-09-08T02:00:00Z" + ) + binding["accepted_result"] = prior + current_task = deepcopy(old_task) + current_task["files"]["write"] = ["src/a.py", "src/b.py"] + current_task["validation"][0]["command"] = "pytest -q tests/current" + previous_identity = { + "artifact_id": "task-001", "revision": OID_A, + "sha256": "1" * 64, "source_tree": OID_B, + } + current_identity = { + "artifact_id": "task-001", "revision": OID_C, + "sha256": "2" * 64, "source_tree": OID_D, + } + review = { + "required": True, + "reviewer_independent": True, + "review_id": "review-current-authority", + "reviewed_head": OID_C, + "review_mode": "initial", + "review_target_kind": "task", + "repair_frontier": None, + "review_reset": { + "prior_review_id": "review-001", + "reason_class": "scope", + "reason": "Current task scope authority changed.", + }, + "target_identity": current_identity, + "reviewer": {"agent_id": "reviewer-current"}, + "verdict": "accept", + "previous_review": { + "review_id": "review-001", + "review_target_kind": "task", + "target_identity": previous_identity, + }, + } + validated_review = SimpleNamespace( + review_id="review-current-authority", + review_mode="initial", + verdict="accepted", + target_identity=current_identity, + repair_frontier=None, + review_reset=review["review_reset"], + reviewer={"agent_id": "reviewer-current"}, + ) + monkeypatch.setattr(execution_context, "load_task_execution_binding", lambda *_: binding) + monkeypatch.setattr(review_runtime, "validate_task_acceptance_review", lambda _review: validated_review) + monkeypatch.setattr( + execution_context, + "capture_repository_evidence", + lambda _root: {"head": OID_C, "tree": OID_D, "status": "clean", "entries": {}}, + ) + + def git_result(arguments, **_kwargs): + if "merge-base" in arguments: + return SimpleNamespace(returncode=0, stdout="", stderr="") + value = OID_D if str(arguments[-1]).endswith("^{tree}") else OID_C + return SimpleNamespace(returncode=0, stdout=value + "\n", stderr="") + + monkeypatch.setattr(execution_context.subprocess, "run", git_result) + persisted: dict[str, object] = {} + monkeypatch.setattr(execution_context, "_persist_binding", lambda value, _root: persisted.update(value)) + monkeypatch.setattr( + execution_context, + "build_accepted_task_result", + lambda *_args, **_kwargs: pytest.fail("executor result replayed"), + ) + + accepted = execution_context.materialize_accepted_task_review( + tmp_path, + current_task, + review, + { + "causal_class": "claim_relevant_drift", + "affected_task": "task-001", + "authorized_lifecycle_action": "rematerialize_accepted_result", + }, + ) + + for field in ( + "baseline_identity", "executor_result_digest", "validation_evidence_ids", + "owner_identity", "knowledge_disposition", + ): + assert accepted[field] == prior[field] + assert accepted["accepted_source"]["head"] == OID_C + assert accepted["accepted_source"]["tree"] == OID_D + assert accepted["authority_projection"] == execution_context._accepted_authority_projection( + current_task, binding, accepted_review=review, owner_identity=prior["owner_identity"] + ) + assert persisted["accepted_result"] == accepted + assert "previous_review" not in repr(accepted) + assert "causal_class" not in repr(accepted) + + def test_legacy_accepted_result_without_disposition_remains_current_for_nonknowledge_consumers( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: From b9882b6a3f0c026d5127fd691a8c7c784abcead0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 19:04:34 +0800 Subject: [PATCH 43/48] fix(orchestration): allow same reviewer after reset --- scripts/orchestration/review_runtime.py | 4 +- tests/test_orchestration_reviews.py | 67 +++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index 81b9a86..afe77ea 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -1302,8 +1302,8 @@ def validate_review_sequence( if (reset is None or reset["prior_review_id"] != previous.review_id or reset["reason_class"] != material_change): raise ReviewContractError("material change requires a recorded fresh initial review reset") - if current.reviewer["agent_id"] == previous.reviewer["agent_id"] or current.reviewer["capability"] != "judgment": - raise ReviewContractError("reset requires a fresh capable independent reviewer identity") + if current.reviewer["capability"] != "judgment": + raise ReviewContractError("reset requires a capable independent judgment reviewer") elif current.review_reset is not None: raise ReviewContractError("review_reset requires a classified material change") return current diff --git a/tests/test_orchestration_reviews.py b/tests/test_orchestration_reviews.py index db14976..e191238 100644 --- a/tests/test_orchestration_reviews.py +++ b/tests/test_orchestration_reviews.py @@ -21,6 +21,7 @@ route_review_verdict, transition_review_finding, validate_contract_instance, + validate_review_sequence, validate_stage_review, validate_stage_reviews, validate_task_acceptance_review, @@ -162,6 +163,72 @@ def test_task_repair_review_binds_authoritative_integrated_stage_predecessor() - validate_task_acceptance_review(invalid) +def test_material_change_reset_allows_same_independent_judgment_reviewer() -> None: + previous = stage_review("plan") + previous.update( + review_mode="initial", + review_target_kind="stage", + repair_frontier=None, + review_reset=None, + ) + current = deepcopy(previous) + current["review_id"] = "review-plan-current" + current["target_identity"] = { + **previous["target_identity"], + "revision": "2", + "sha256": "2" * 64, + } + current["review_reset"] = { + "prior_review_id": previous["review_id"], + "reason_class": "scope", + "reason": "The accepted plan scope materially changed.", + } + + validated = validate_review_sequence( + current, previous_review=previous, material_change="scope" + ) + + assert validated.reviewer["agent_id"] == previous["reviewer"]["agent_id"] + assert validated.review_reset["prior_review_id"] == previous["review_id"] + + +@pytest.mark.parametrize( + ("field", "value", "message"), + [ + ("authorship", "present", "accepted review requires reviewer.authorship"), + ("capability", "standard", "judgment reviewer"), + ], +) +def test_material_change_reset_still_rejects_nonindependent_or_nonjudgment_reviewer( + field: str, value: str, message: str +) -> None: + previous = stage_review("plan") + previous.update( + review_mode="initial", + review_target_kind="stage", + repair_frontier=None, + review_reset=None, + ) + current = deepcopy(previous) + current["review_id"] = "review-plan-current" + current["target_identity"] = { + **previous["target_identity"], + "revision": "2", + "sha256": "2" * 64, + } + current["review_reset"] = { + "prior_review_id": previous["review_id"], + "reason_class": "scope", + "reason": "The accepted plan scope materially changed.", + } + current["reviewer"][field] = value + + with pytest.raises(ReviewContractError, match=message): + validate_review_sequence( + current, previous_review=previous, material_change="scope" + ) + + @pytest.mark.parametrize( ("finding_class", "expected"), [ From f809e886a99609f66b6144adb5c97358f97455c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 19:43:07 +0800 Subject: [PATCH 44/48] test(orchestration): allow reviewer reuse after reset --- .../test_orchestration_context_projection.py | 5 +++-- tests/test_orchestration_review_frontier.py | 21 ++++++++++++++++--- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/tests/test_orchestration_context_projection.py b/tests/test_orchestration_context_projection.py index cad9c67..60a77d7 100644 --- a/tests/test_orchestration_context_projection.py +++ b/tests/test_orchestration_context_projection.py @@ -742,8 +742,9 @@ def review(review_id: str, index: int, verdict: str) -> dict[str, object]: same_reviewer["review_chain"][-1]["handoff_sha256"] = hashlib.sha256( final_handoff_path.read_bytes() ).hexdigest() - with pytest.raises(SystemExit, match="fresh|reviewer|independent"): - execution_context._accepted_dependency_paths(task, root, [same_reviewer]) + assert execution_context._accepted_dependency_paths(task, root, [same_reviewer]) == { + dependency_path, repair_path, final_path + } final_handoff_path.write_bytes(original_final_bytes) assert execution_context._accepted_dependency_paths(task, root, [descriptor]) == { diff --git a/tests/test_orchestration_review_frontier.py b/tests/test_orchestration_review_frontier.py index 242585e..2a2dcf0 100644 --- a/tests/test_orchestration_review_frontier.py +++ b/tests/test_orchestration_review_frontier.py @@ -130,12 +130,27 @@ def test_rf_04_material_change_requires_fresh_initial_review(reason_class: str) assert review_runtime.validate_review_sequence(reset, previous_review=prior, material_change=reason_class).review_mode == "initial" -def test_rf_05_reset_rejects_reused_repair_reviewer_identity() -> None: +def test_rf_05_reset_allows_same_independent_reviewer_and_keeps_safeguards() -> None: prior, repair = repair_pair() reset = review(target=repair["target_identity"], agent=prior["reviewer"]["agent_id"]) reset["review_reset"] = {"prior_review_id": prior["review_id"], "reason_class": "scope", "reason": "scope changed"} - with pytest.raises(review_runtime.ReviewContractError, match="fresh capable independent reviewer"): - review_runtime.validate_review_sequence(reset, previous_review=prior, material_change="scope") + assert review_runtime.validate_review_sequence( + reset, previous_review=prior, material_change="scope" + ).reviewer["agent_id"] == prior["reviewer"]["agent_id"] + + participating = deepcopy(reset) + participating["reviewer"]["repair_participation"] = "present" + with pytest.raises(review_runtime.ReviewContractError, match="repair_participation"): + review_runtime.validate_review_sequence( + participating, previous_review=prior, material_change="scope" + ) + + nonjudgment = deepcopy(reset) + nonjudgment["reviewer"]["capability"] = "standard" + with pytest.raises(review_runtime.ReviewContractError, match="judgment reviewer"): + review_runtime.validate_review_sequence( + nonjudgment, previous_review=prior, material_change="scope" + ) def test_rf_06_repair_rejects_stale_or_relabelled_identity() -> None: From c43802287e181b76b84dce6b859f8a5467a6c36b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 21:16:36 +0800 Subject: [PATCH 45/48] fix(orchestration): allow independent reviewer reuse --- .../assets/orchestration/contract/task-v1.md | 2 +- tests/test_orchestration_workflow_contracts.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/references/assets/orchestration/contract/task-v1.md b/references/assets/orchestration/contract/task-v1.md index 19e5d37..d739ee8 100644 --- a/references/assets/orchestration/contract/task-v1.md +++ b/references/assets/orchestration/contract/task-v1.md @@ -149,7 +149,7 @@ A legacy 3-column `Command or inspection | Proves | Expected` row without YAML ` - A newly authored task acceptance record exposes `review_mode: initial|repair` and `review_target_kind: task`. Legacy records without these fields are tolerated only as initial-review migration input. - When `task_fit_check.result` is `repaired`, completion requires `review_mode: repair`, the native review envelope fields, and exactly one `previous_review`. The closed `repair_frontier` binds that predecessor's review ID, blocking finding IDs, previous and repaired target identities, affected boundaries, and frozen evidence identity. Whole review history is not embedded or reacquired. - Both the current and previous task-review records retain `required: true`, `reviewer_independent: true`, and `review_target_kind: task`; the adapter does not infer or overwrite those ownership facts. The accepted repaired target names the completed task and its `source_tree` plus `reviewed_head` must equal the helper-observed Git tree and head. -- Material redesign or changed authority, scope, acceptance, decomposition, or validation allocation uses a fresh `initial` review with `review_reset`; it may not reuse the repair reviewer identity. +- Material redesign or changed authority, scope, acceptance, decomposition, or validation allocation requires a fresh `initial` review with `review_reset` bound to the prior review, classified reason, and current target and evidence. The reviewer may reuse the same agent identity when judgment-capable and independent by authorship/repair/decision/deliberation participation and review provenance; identity rotation is not a freshness requirement. ## Planning verification diff --git a/tests/test_orchestration_workflow_contracts.py b/tests/test_orchestration_workflow_contracts.py index f28e339..f3b66f6 100644 --- a/tests/test_orchestration_workflow_contracts.py +++ b/tests/test_orchestration_workflow_contracts.py @@ -927,6 +927,20 @@ def test_task_contract_compiles_methodology_capability_and_review() -> None: assert token in contract +def test_task_contract_defines_material_review_freshness_without_identity_rotation() -> None: + contract = read("references/assets/orchestration/contract/task-v1.md") + + for token in [ + "`review_reset` bound to the prior review, classified reason, and current target and evidence", + "may reuse the same agent identity", + "judgment-capable", + "authorship/repair/decision/deliberation participation", + "review provenance", + ]: + assert token in contract + assert "may not reuse the repair reviewer identity" not in contract + + def test_executor_result_contract_carries_acceptance_review() -> None: contract = read("references/assets/orchestration/contract/handoff-executor-result-v1.md") for token in [ From 171bb018c0ebf33d1dea6c87d20bd07942276643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Tue, 8 Sep 2026 21:46:08 +0800 Subject: [PATCH 46/48] feat(orchestration): publish review authority --- .../assets/orchestration/contract/task-v1.md | 3 + references/assets/orchestration/workflow.md | 21 +- rules/orchestration/orch-review-completion.md | 2 + scripts/orchestration/execution_context.py | 108 ++++++++-- scripts/orchestration/review_runtime.py | 185 +++++++++++++++++- scripts/work-bundle/reviewer_workspace.py | 92 +++++++-- skills/orch-execute-plan/SKILL.md | 6 +- skills/orch-review-plan/SKILL.md | 2 + tests/test_orchestration_accepted_result.py | 41 +++- tests/test_orchestration_execution_context.py | 23 +++ tests/test_orchestration_plan_return.py | 2 +- .../test_orchestration_planning_scenarios.py | 2 +- tests/test_orchestration_review_frontier.py | 4 +- tests/test_orchestration_reviews.py | 47 ++++- tests/test_reviewer_workspace.py | 71 +++++++ 15 files changed, 555 insertions(+), 54 deletions(-) diff --git a/references/assets/orchestration/contract/task-v1.md b/references/assets/orchestration/contract/task-v1.md index d739ee8..659330c 100644 --- a/references/assets/orchestration/contract/task-v1.md +++ b/references/assets/orchestration/contract/task-v1.md @@ -150,6 +150,9 @@ A legacy 3-column `Command or inspection | Proves | Expected` row without YAML ` - When `task_fit_check.result` is `repaired`, completion requires `review_mode: repair`, the native review envelope fields, and exactly one `previous_review`. The closed `repair_frontier` binds that predecessor's review ID, blocking finding IDs, previous and repaired target identities, affected boundaries, and frozen evidence identity. Whole review history is not embedded or reacquired. - Both the current and previous task-review records retain `required: true`, `reviewer_independent: true`, and `review_target_kind: task`; the adapter does not infer or overwrite those ownership facts. The accepted repaired target names the completed task and its `source_tree` plus `reviewed_head` must equal the helper-observed Git tree and head. - Material redesign or changed authority, scope, acceptance, decomposition, or validation allocation requires a fresh `initial` review with `review_reset` bound to the prior review, classified reason, and current target and evidence. The reviewer may reuse the same agent identity when judgment-capable and independent by authorship/repair/decision/deliberation participation and review provenance; identity rotation is not a freshness requirement. +- Task and stage review results are first-class review-store records. Lifecycle admission takes only `{review_id, sha256}` plus the expected current target; it revalidates the native immutable reviewer-run receipt before exposing a verdict or selecting a finding. Bare output, receipt, or finding objects are non-authoritative. +- The task-review product candidate contains compiled task authority, exact source/diff identity, harness-owned validation observations, unresolved product concerns, and task-local disposition. Executor-handoff structure and publication/status/archive bookkeeping remain controller-owned and are excluded from reviewer judgment. +- A stored post-execution task repair review may recompute the compact accepted result while preserving its executor-result digest, validation evidence identities, owner, baseline, and knowledge disposition. This path performs no executor redispatch, replacement handoff, validation rerun, or review-history embedding. ## Planning verification diff --git a/references/assets/orchestration/workflow.md b/references/assets/orchestration/workflow.md index 8fe2e01..2af8bf6 100644 --- a/references/assets/orchestration/workflow.md +++ b/references/assets/orchestration/workflow.md @@ -109,9 +109,13 @@ not lifecycle admission. The gate resolves the controller-owned store through `reviewer_runtime_root(workspace_root)` under `~/.work-bundle/reviewer-runtime/workspaces/`; the envelope cannot select an arbitrary receipt path or store. -Before workspace creation, the controller adds `stage_review_context` to the direct +Before workspace creation, the controller adds `stage_review_context` to a stage direct evidence packet: `stage`, `target_identity`, `target_locator` (a copied control artifact), `agent_id`, `capability`, `execution_id`, and `evidence_mode`. +For task review it instead adds native `task_review_context`, binding the task target, +review mode/frontier or reset, reviewer identity/capability, execution identity, and +evidence mode. Workspace creation admits it only when the source checkout is clean and +its exact HEAD/tree still equal that task target. The current sandbox denies live source/control access, so its packet builder derives `evidence_mode`; requesting `direct_source` does not grant it. A mechanically complete `stage-evidence-manifest-v1` yields `reproducible_snapshot`; missing evidence yields @@ -145,7 +149,10 @@ workspace creation checks it again. Run the worker with `reviewer-process-run` u that runtime root. Its stdout must be exactly one stage-review JSON object, without `reviewer_run`; the native publisher verifies it against the frozen context and binds its canonical digest into the receipt. The controller then attaches the run -ID and SHA-256 of the immutable receipt bytes to that exact result. +ID and SHA-256 of the immutable receipt bytes to that exact result and publishes the +task-or-stage envelope as a read-only review-store record. Verdict admission and +named-finding routing resolve only that stored reference and recheck its receipt and +current target; bare stdout, unattached receipts, and bare findings remain observations. The lifecycle gate verifies review ID, exact result/target/profile, successful completion, sandbox/network/write boundary, and immutable packet/profile/event @@ -170,6 +177,12 @@ remain single-flight. Publication rejects an intervening mutation epoch or expir freshness. Reservation lock files are retained to avoid splitting concurrent waiters; they are runtime artifacts, not source inputs or a separate cache subsystem. +Task code review consumes one product candidate compiled from task authority, exact +source/diff identity, harness-owned validation observations, unresolved product +concerns, and task-local disposition. Executor-handoff schema and publication, +status, and archive bookkeeping remain controller preconditions and never become +product findings. + The **acceptance once** lifecycle rule makes the harness strongly verify binding, source/scope, subagent ownership, validation, and required review, then persists one compact accepted result. Dependency release, finalization, resume, and archive consume @@ -177,6 +190,10 @@ that result plus a current harness observation while its identity and freshness they do not replay transient acceptance evidence or historical handoff chains. A status-only or append-only evidence change neither invalidates the canonical semantic plan projection nor causes a terminal rerun. +A later stored task repair review recomposes this compact result through the existing +materializer while preserving executor-result, validation, owner, baseline, and +knowledge authority; it performs no executor redispatch, handoff rewrite, validation +rerun, or review-history embedding. Capability context projects trusted intent/evaluation seeds through the existing typed-relation traversal (`light`: 1 hop, `standard`: 2, `deep`: 4), bounded by diff --git a/rules/orchestration/orch-review-completion.md b/rules/orchestration/orch-review-completion.md index a741877..e8aa36d 100644 --- a/rules/orchestration/orch-review-completion.md +++ b/rules/orchestration/orch-review-completion.md @@ -17,6 +17,8 @@ Keep final review focused on whether the WorkBundle workflow completed correctly ## Must - Confirm each required task review compared the accepted Truth Basis, implementation, test oracle, and task-local knowledge disposition before accepting the task. +- Admit a task-or-stage verdict or route a finding only from its immutable review-store reference after native reviewer-run receipt and exact current-target validation. Treat bare reviewer output, unattached receipts, and bare findings as observations only. +- Keep product review candidates limited to task authority, exact source/diff identity, harness-owned observations, unresolved product concerns, and task-local disposition. Handoff schema and publication/archive bookkeeping remain controller audit concerns and cannot become product findings. - On reviewer infrastructure or provider failure, replace only the reviewer against the same immutable review package; preserve source identity, validation evidence, plan decomposition, and review frontier. - For a finding-scoped repair review under unchanged authority, carry exactly the previous finding/evidence frontier and review only the repaired identity and affected boundaries. Reset to an initial frontier only after a material authority, scope, acceptance, decomposition, or validation-allocation change. - Check that declared completion evidence corresponds to the compiled Truth Basis, source IDs, expected delta, and remaining AUTH constraints. diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index dcbc866..af93a85 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -1825,7 +1825,7 @@ def _load_materialized_accepted_task_result( def materialize_accepted_task_review( control_root: Path, task: Mapping[str, Any], - review: Mapping[str, Any], + review_reference: Mapping[str, Any], causal_classification: Mapping[str, Any], *, accepted_at: str | None = None, @@ -1851,9 +1851,18 @@ def materialize_accepted_task_review( if task.get("review_required") is not True: raise SystemExit("accepted task repair review requires mandatory task review authority") try: - from review_runtime import ReviewContractError, validate_task_acceptance_review + from review_runtime import ( + ReviewContractError, + load_stored_review, + stored_review_target_identity, + ) - validated_review = validate_task_acceptance_review(review) + target_identity = stored_review_target_identity(root, review_reference) + review, validated_review = load_stored_review( + root, + review_reference, + current_target_identity=target_identity, + ) except (ReviewContractError, KeyError, TypeError, ValueError) as error: raise SystemExit(f"Accepted task repair review is invalid: {error}") from error task_id = str(task.get("task_id") or "") @@ -4823,6 +4832,53 @@ def _markdown_items(values: list[Any], empty: str = "None.") -> list[str]: return result +def build_product_review_candidate( + *, + task: Mapping[str, Any], + base: str, + head: str, + diff: str, + changed_files: Sequence[str], + changed_symbols: Sequence[str], + validation_observations: Sequence[Mapping[str, Any]], + knowledge_disposition: Mapping[str, Any], + unresolved: Sequence[Any] = (), +) -> dict[str, Any]: + """Build the sole semantic task-review input, excluding transport bookkeeping.""" + + candidate = { + "task_authority": { + "task_id": str(task.get("task_id") or ""), + "plan_id": str(task.get("plan_id") or ""), + "goal": task.get("goal"), + "requirements": list(_as_list(task.get("requirements"))), + "constraints": list(_as_list(task.get("constraints"))), + "truth_basis": task.get("truth_basis", {}), + "semantic_authority": task.get("semantic_authority", {}), + "evidence_capability": task.get("evidence_capability", {}), + "files": task.get("files", {}), + "interfaces": task.get("interfaces", {}), + "allocated_rules": list(_as_list(task.get("allocated_rules"))), + "methodology": task.get("methodology", {}), + }, + "source": { + "base": base, + "head": head, + "diff": diff, + "changed_files": list(changed_files), + "changed_symbols": list(changed_symbols), + }, + "validation_observations": [dict(item) for item in validation_observations], + "knowledge_disposition": dict(knowledge_disposition), + "unresolved": list(unresolved), + } + forbidden = {"handoff", "acceptance_review", "publication", "reviewer_run"} + if forbidden.intersection(candidate): + raise SystemExit("product review candidate contains publication bookkeeping") + _assert_no_credential_values(candidate, "product review candidate") + return candidate + + def build_review_package(args: argparse.Namespace) -> Path: if not args.handoff or not args.base or not args.head: raise SystemExit("build-review-package requires --handoff, --base, and --head") @@ -4933,60 +4989,74 @@ def build_review_package(args: argparse.Namespace) -> Path: } _assert_no_credential_values(evidence, "review evidence") - required = [f"Goal: {task.get('goal')}", *task.get("requirements", []), *task.get("constraints", [])] - interfaces = task.get("interfaces", {}) + candidate = build_product_review_candidate( + task=task, + base=base, + head=head, + diff=diff, + changed_files=name_status, + changed_symbols=symbols, + validation_observations=evidence_projection, + knowledge_disposition=knowledge_disposition, + unresolved=unresolved, + ) + authority = candidate["task_authority"] + source = candidate["source"] + + required = [f"Goal: {authority.get('goal')}", *authority.get("requirements", []), *authority.get("constraints", [])] + interfaces = authority.get("interfaces", {}) if isinstance(interfaces, dict): required.extend(_as_list(interfaces.get("consumes"))) required.extend(_as_list(interfaces.get("produces"))) assertions = [ - *[f"rule {item['id']}: {item['requirement']}" for item in task.get("allocated_rules", [])], - f"methodology {task['methodology'].get('primary')}: skills {', '.join(map(str, task['methodology'].get('skills', []))) or 'none'}", + *[f"rule {item['id']}: {item['requirement']}" for item in authority.get("allocated_rules", [])], + f"methodology {authority['methodology'].get('primary')}: skills {', '.join(map(str, authority['methodology'].get('skills', []))) or 'none'}", ] - allowed_scope = list(dict.fromkeys([*task.get("files", {}).get("write", []), *task.get("files", {}).get("read", [])])) + allowed_scope = list(dict.fromkeys([*authority.get("files", {}).get("write", []), *authority.get("files", {}).get("read", [])])) lines = [ "# Task Review Package", "", f"Task: {task_id}", - f"Base: {base}", - f"Head: {head}", + f"Base: {source['base']}", + f"Head: {source['head']}", f"Review mode: {review_mode}", "", "## Required behavior", *_markdown_items(required), "", "## Accepted Truth Basis", - *_markdown_items([task.get("truth_basis", {})]), + *_markdown_items([authority.get("truth_basis", {})]), "", "## Semantic authority", - *_markdown_items([task.get("semantic_authority", {})]), + *_markdown_items([authority.get("semantic_authority", {})]), "", "## Evidence capability", - *_markdown_items([task.get("evidence_capability", {})]), + *_markdown_items([authority.get("evidence_capability", {})]), "", "## Allowed scope", *_markdown_items(allowed_scope), "", "## Changed files", - *_markdown_items(name_status), + *_markdown_items(source["changed_files"]), "", "## Changed symbols", - *_markdown_items(symbols), + *_markdown_items(source["changed_symbols"]), "", "## Validation reported", - *_markdown_items(evidence_projection), + *_markdown_items(candidate["validation_observations"]), "", "## Knowledge disposition", - *_markdown_items([knowledge_disposition]), + *_markdown_items([candidate["knowledge_disposition"]]), "", "## Allocated rule and methodology assertions", *_markdown_items(assertions), "", "## Unresolved concerns", - *_markdown_items(unresolved), + *_markdown_items(candidate["unresolved"]), "", "## Diff", "```diff", - diff.rstrip(), + source["diff"].rstrip(), "```", ] if repair_frontier is not None: diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index afe77ea..05d482b 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -396,6 +396,23 @@ def _known_execution_ids(root: Path, stage: str, identity: Mapping[str, Any]) -> return ids +def _known_task_execution_ids(root: Path, task_id: str) -> set[str]: + ids: set[str] = set() + runtime = root / ".work-bundle/runtime/execution" + for path in runtime.glob(f"*/{task_id}/execution-binding.json"): + if not path.resolve().is_relative_to(runtime.resolve()): + raise ReviewContractError("task review provenance binding path escapes store") + binding = json.loads(path.read_text()) + for field in ("execution_id",): + if binding.get(field): + ids.add(str(binding[field])) + ownership = binding.get("ownership") if isinstance(binding.get("ownership"), Mapping) else {} + for field in ("run_id", "agent_id"): + if ownership.get(field): + ids.add(str(ownership[field])) + return ids + + def _validate_reviewer_run(root: Path, review: Mapping[str, Any]) -> None: reference = review.get("reviewer_run") if not isinstance(reference, dict) or set(reference) != {"run_id", "sha256"}: @@ -422,7 +439,9 @@ def immutable_file(target: Path) -> bytes: def canonical(value: Any) -> str: return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode()).hexdigest() result = {key: value for key, value in review.items() if key != "reviewer_run"} - context = _mapping(receipt.get("stage_review_context", {}), "reviewer-run provenance context") + kind = str(review.get("review_target_kind") or "stage") + context_key = "task_review_context" if kind == "task" else "stage_review_context" + context = _mapping(receipt.get(context_key, {}), "reviewer-run provenance context") mode = "direct_source" if review["evidence"]["mode"] == "direct" else review["evidence"]["mode"] review_context = { "review_mode": review.get("review_mode", "initial"), @@ -439,8 +458,9 @@ def canonical(value: Any) -> str: if (receipt.get("schema") != "reviewer-process-receipt-v1" or receipt.get("run_id") != run_id or receipt.get("review_id") != review["review_id"] or receipt.get("status") != "passed" or receipt.get("exit_code") != 0 or receipt.get("review_result_sha256") != canonical(result) - or receipt.get("packet_sha256") != canonical(packet) or packet.get("stage_review_context") != context - or context.get("target_identity") != review["target_identity"] or context.get("stage") != review["stage"] + or receipt.get("packet_sha256") != canonical(packet) or packet.get(context_key) != context + or context.get("target_identity") != review["target_identity"] + or (kind == "stage" and context.get("stage") != review["stage"]) or context.get("agent_id") != review["reviewer"]["agent_id"] or context.get("evidence_mode") != review["reviewer"]["context_origin"] or context.get("capability") != review["reviewer"]["capability"] or context.get("evidence_mode") != mode @@ -450,8 +470,13 @@ def canonical(value: Any) -> str: or receipt.get("sandbox_profile_sha256") != hashlib.sha256(immutable_file(path.with_suffix(".profile.sb"))).hexdigest() or receipt.get("event_log_sha256") != hashlib.sha256(immutable_file(path.with_suffix(".events.jsonl"))).hexdigest()): raise ReviewContractError("reviewer-run provenance does not bind this accepted review") - known = _known_execution_ids(root, str(review["stage"]), review["target_identity"]) - validate_stage_evidence(root, context, packet) + known = ( + _known_task_execution_ids(root, str(review["target_identity"]["artifact_id"])) + if kind == "task" + else _known_execution_ids(root, str(review["stage"]), review["target_identity"]) + ) + if kind == "stage": + validate_stage_evidence(root, context, packet) if run_id in known or context["execution_id"] in known: raise ReviewContractError("reviewer-run provenance overlaps author/repair execution") @@ -982,7 +1007,7 @@ def validate_review_finding(value: Mapping[str, Any]) -> ReviewFindingV1: ) -def route_review_verdict( +def _route_review_finding( value: Mapping[str, Any], *, previous_scope_expansions: int = 0, affected_region: Mapping[str, Any] | None = None, unaffected_evidence_identities: Sequence[Mapping[str, Any]] = (), @@ -1045,6 +1070,154 @@ def route_review_verdict( return result +def _validated_review_envelope(value: Mapping[str, Any]) -> StageReviewV1: + """Validate one bounded task-or-stage review without walking older history.""" + + if value.get("review_target_kind") == "task": + return validate_task_acceptance_review(value) + current = validate_stage_review(value) + previous = value.get("previous_review") + if current.review_mode == "repair": + if not isinstance(previous, Mapping) or "previous_review" in previous: + raise ReviewContractError("stage repair review requires exactly one previous_review") + return validate_review_sequence(value, previous_review=previous) + if current.review_reset is not None: + if not isinstance(previous, Mapping) or "previous_review" in previous: + raise ReviewContractError("stage reset review requires exactly one previous_review") + return validate_review_sequence( + value, + previous_review=previous, + material_change=str(current.review_reset["reason_class"]), + ) + return validate_review_sequence(value) + + +def _review_store_path(root: Path, review_id: str) -> Path: + store = root.expanduser().resolve() / ".work-bundle/orchestration/reviews" + path = (store / f"{_identifier(review_id, 'review_id')}.json").resolve(strict=False) + if not path.is_relative_to(store.resolve()): + raise ReviewContractError("stored review path escapes review store") + return path + + +def publish_review( + root: Path, + review: Mapping[str, Any], + *, + current_target_identity: Mapping[str, Any], +) -> dict[str, str]: + """Publish a natively receipted current task-or-stage review exactly once.""" + + record = dict(_mapping(review, "review publication")) + validated = _validated_review_envelope(record) + current = dict(_target_identity(current_target_identity, "current_target_identity")) + if validated.target_identity != current: + raise ReviewContractError("review publication target is not current") + _validate_reviewer_run(root.expanduser().resolve(), record) + path = _review_store_path(root, validated.review_id) + content = (json.dumps(record, indent=2, sort_keys=True) + "\n").encode("utf-8") + digest = hashlib.sha256(content).hexdigest() + path.parent.mkdir(parents=True, exist_ok=True) + if path.exists(): + if path.is_symlink() or path.read_bytes() != content: + raise ReviewContractError("stored review identity collision") + else: + with path.open("xb") as stream: + stream.write(content) + path.chmod(0o444) + return {"review_id": validated.review_id, "sha256": digest} + + +def load_stored_review( + root: Path, + reference: Mapping[str, Any], + *, + current_target_identity: Mapping[str, Any], +) -> tuple[dict[str, Any], StageReviewV1]: + """Load stored review authority and revalidate its receipt and current target.""" + + if not isinstance(reference, Mapping) or set(reference) != {"review_id", "sha256"}: + raise ReviewContractError("stored review reference is required") + path = _review_store_path(root, str(reference.get("review_id") or "")) + if ( + path.is_symlink() + or not path.is_file() + or path.stat().st_mode & 0o222 + ): + raise ReviewContractError("stored review is missing or mutable") + raw = path.read_bytes() + if hashlib.sha256(raw).hexdigest() != reference.get("sha256"): + raise ReviewContractError("stored review digest mismatch") + record = dict(_mapping(json.loads(raw), "stored review")) + validated = _validated_review_envelope(record) + current = dict(_target_identity(current_target_identity, "current_target_identity")) + if validated.target_identity != current: + raise ReviewContractError("stored review target is not current") + _validate_reviewer_run(root.expanduser().resolve(), record) + return record, validated + + +def stored_review_target_identity( + root: Path, reference: Mapping[str, Any] +) -> dict[str, Any]: + """Read only a digest-bound target hint; this does not admit review authority.""" + + if not isinstance(reference, Mapping) or set(reference) != {"review_id", "sha256"}: + raise ReviewContractError("stored review reference is required") + path = _review_store_path(root, str(reference.get("review_id") or "")) + if path.is_symlink() or not path.is_file() or path.stat().st_mode & 0o222: + raise ReviewContractError("stored review is missing or mutable") + raw = path.read_bytes() + if hashlib.sha256(raw).hexdigest() != reference.get("sha256"): + raise ReviewContractError("stored review digest mismatch") + record = _mapping(json.loads(raw), "stored review") + return dict(_target_identity(record.get("target_identity"), "stored review target_identity")) + + +def route_stored_review_verdict( + root: Path, + review_reference: Mapping[str, Any], + *, + current_target_identity: Mapping[str, Any], + finding_id: str | None = None, + previous_scope_expansions: int = 0, + affected_region: Mapping[str, Any] | None = None, + unaffected_evidence_identities: Sequence[Mapping[str, Any]] = (), + original_binding_identity: Mapping[str, Any] | None = None, + original_baseline_identity: Mapping[str, Any] | None = None, +) -> dict[str, Any]: + """Expose a verdict or route one finding only from stored current authority.""" + + record, validated = load_stored_review( + root, review_reference, current_target_identity=current_target_identity + ) + if finding_id is None: + return { + "review_id": validated.review_id, + "verdict": validated.verdict, + "target_identity": dict(validated.target_identity), + } + matches = [ + item for item in record.get("findings", []) + if isinstance(item, Mapping) and item.get("finding_id") == finding_id + ] + if len(matches) != 1: + raise ReviewContractError("stored review does not contain exactly one selected finding") + return _route_review_finding( + matches[0], + previous_scope_expansions=previous_scope_expansions, + affected_region=affected_region, + unaffected_evidence_identities=unaffected_evidence_identities, + original_binding_identity=original_binding_identity, + original_baseline_identity=original_baseline_identity, + ) + + +# The public legacy name now enforces stored authority too. Pure classification tests +# use the explicitly private helper and cannot be mistaken for lifecycle routing. +route_review_verdict = route_stored_review_verdict + + def resume_plan_return( value: Mapping[str, Any], *, workspace_root: Path, diff --git a/scripts/work-bundle/reviewer_workspace.py b/scripts/work-bundle/reviewer_workspace.py index a5149ca..6ddd6d3 100644 --- a/scripts/work-bundle/reviewer_workspace.py +++ b/scripts/work-bundle/reviewer_workspace.py @@ -71,6 +71,54 @@ def _validate_stage_context(context: object) -> dict[str, object]: return context +def _validate_task_context(context: object) -> dict[str, object]: + fields = { + "target_identity", "agent_id", "capability", "execution_id", "evidence_mode", + "review_mode", "review_target_kind", "repair_frontier", "review_reset", + } + if not isinstance(context, dict) or set(context) != fields: + raise ReviewerWorkspaceError("WB_REVIEW_TASK_CONTEXT_INVALID") + if ( + context["review_target_kind"] != "task" + or context["capability"] not in {"standard", "judgment"} + or context["evidence_mode"] not in {"direct_source", "reproducible_snapshot", "packet_only"} + or not all(isinstance(context[key], str) and context[key] for key in ("agent_id", "execution_id")) + ): + raise ReviewerWorkspaceError("WB_REVIEW_TASK_CONTEXT_INVALID") + _review_runtime()._target_identity(context["target_identity"]) + try: + mode = _review_runtime()._enum(context["review_mode"], _review_runtime().REVIEW_MODES, "review_mode") + if mode == "repair": + _review_runtime()._repair_frontier(context["repair_frontier"]) + if context["review_reset"] is not None: + raise ValueError("repair reset") + elif context["repair_frontier"] is not None: + raise ValueError("initial frontier") + except (ValueError, TypeError): + raise ReviewerWorkspaceError("WB_REVIEW_TASK_CONTEXT_INVALID") from None + return context + + +def _validate_task_source_identity(source_root: Path, context: dict[str, object]) -> None: + identity = context["target_identity"] + assert isinstance(identity, dict) + head = subprocess.run( + ["git", "-C", str(source_root), "rev-parse", "HEAD"], capture_output=True, text=True + ) + tree = subprocess.run( + ["git", "-C", str(source_root), "rev-parse", "HEAD^{tree}"], capture_output=True, text=True + ) + status = subprocess.run( + ["git", "-C", str(source_root), "status", "--porcelain=v1"], capture_output=True, text=True + ) + if ( + head.returncode or tree.returncode or status.returncode or status.stdout + or head.stdout.strip() != identity.get("revision") + or tree.stdout.strip() != identity.get("source_tree") + ): + raise ReviewerWorkspaceError("WB_REVIEW_TASK_TARGET_MISMATCH") + + class ReviewerWorkspaceError(RuntimeError): def __init__(self, code: str, result: dict[str, object] | None = None) -> None: super().__init__(code) @@ -158,6 +206,7 @@ def build_direct_evidence_packet( sentinels: list[str], network_state: str, stage_review_context: dict[str, object] | None = None, + task_review_context: dict[str, object] | None = None, ) -> dict[str, object]: """Copy only named direct evidence into a location-free packet. @@ -222,6 +271,8 @@ def build_direct_evidence_packet( sentinel_records.append( {"locator": f"{scope}:{relative.as_posix()}", "sha256": _sha256_bytes(candidate.read_bytes())} ) + if stage_review_context is not None and task_review_context is not None: + raise ReviewerWorkspaceError("WB_REVIEW_CONTEXT_AMBIGUOUS") stage_fields = {} if stage_review_context is not None: context = dict(_validate_stage_context(stage_review_context)) @@ -230,6 +281,10 @@ def build_direct_evidence_packet( # is a reproducible snapshot; a caller's direct-source label grants nothing. context["evidence_mode"] = "packet_only" if manifest["missing"] else "reproducible_snapshot" stage_fields = {"stage_review_context": context, "stage_evidence_manifest": manifest} + elif task_review_context is not None: + context = dict(_validate_task_context(task_review_context)) + _validate_task_source_identity(source_root, context) + stage_fields = {"task_review_context": context} return { "schema": "review-direct-evidence-packet-v1", **stage_fields, @@ -412,6 +467,9 @@ def create_reviewer_workspace( mode = "packet_only" if manifest["missing"] else "reproducible_snapshot" if packet.get("stage_evidence_manifest") != manifest or context["evidence_mode"] != mode: raise ReviewerWorkspaceError("WB_REVIEW_STAGE_EVIDENCE_MISMATCH") + elif "task_review_context" in packet: + context = _validate_task_context(packet["task_review_context"]) + _validate_task_source_identity(effective_source, context) try: workspace.mkdir(parents=True) scope_digests: dict[str, list[str]] = {"source": [], "control": []} @@ -673,19 +731,30 @@ def run_sandboxed_reviewer(workspace: Path, argv: list[str]) -> dict[str, object "started_at": started_at, "completed_at": datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), } - if "stage_review_context" in packet: - context = _validate_stage_context(packet["stage_review_context"]) + context_key = "stage_review_context" if "stage_review_context" in packet else ( + "task_review_context" if "task_review_context" in packet else None + ) + if context_key is not None: + context = ( + _validate_stage_context(packet[context_key]) + if context_key == "stage_review_context" + else _validate_task_context(packet[context_key]) + ) try: review = json.loads(completed.stdout) - validated = _review_runtime().validate_stage_review(review) + validated = ( + _review_runtime().validate_stage_review(review) + if context_key == "stage_review_context" + else _review_runtime().validate_task_acceptance_review(review) + ) except (ValueError, TypeError) as error: raise ReviewerWorkspaceError("WB_REVIEW_STAGE_OUTPUT_INVALID") from error mode = "direct_source" if validated.evidence["mode"] == "direct" else validated.evidence["mode"] review_context = { - "review_mode": validated.review_mode, - "review_target_kind": validated.review_target_kind, - "repair_frontier": validated.repair_frontier, - "review_reset": validated.review_reset, + "review_mode": review.get("review_mode", "initial"), + "review_target_kind": review.get("review_target_kind", "stage"), + "repair_frontier": review.get("repair_frontier"), + "review_reset": review.get("review_reset"), } packet_context = { "review_mode": context.get("review_mode", "initial"), @@ -694,18 +763,19 @@ def run_sandboxed_reviewer(workspace: Path, argv: list[str]) -> dict[str, object "review_reset": context.get("review_reset"), } if ("reviewer_run" in review or validated.review_id != review_id - or validated.stage != context["stage"] or validated.target_identity != context["target_identity"] + or (context_key == "stage_review_context" and validated.stage != context["stage"]) + or validated.target_identity != context["target_identity"] or validated.reviewer["agent_id"] != context["agent_id"] or validated.reviewer["context_origin"] != context["evidence_mode"] or validated.reviewer["capability"] != context["capability"] or mode != context["evidence_mode"] or review_context != packet_context): raise ReviewerWorkspaceError("WB_REVIEW_STAGE_OUTPUT_MISMATCH") - if validated.verdict == "accepted": + if validated.verdict == "accepted" and context_key == "stage_review_context": try: _review_runtime().validate_stage_evidence(workspace / "evidence/control", context, packet) except (ValueError, OSError, SystemExit) as error: raise ReviewerWorkspaceError("WB_REVIEW_STAGE_EVIDENCE_INCOMPLETE") from error - receipt["stage_review_context"] = context + receipt[context_key] = context receipt["review_result_sha256"] = _canonical_digest(review) receipt["isolation"] = {"mechanism": "sandbox-exec", "network": "denied", "write_scope": "scratch"} receipt_path = (runtime_root / "receipts" / "reviewer-process" / f"{run_id}.json").resolve(strict=False) @@ -725,7 +795,7 @@ def run_sandboxed_reviewer(workspace: Path, argv: list[str]) -> dict[str, object receipt_path.chmod(0o400) reference = {"run_id": run_id, "sha256": _sha256_bytes(receipt_path.read_bytes())} return {**receipt, "receipt_path": str(receipt_path), "event_log_path": sealed["event_log_path"], - **({"reviewer_run": reference} if "stage_review_context" in packet else {})} + **({"reviewer_run": reference} if context_key is not None else {})} def _execute_reviewer_request(workspace: Path, request: dict[str, object]) -> dict[str, object]: diff --git a/skills/orch-execute-plan/SKILL.md b/skills/orch-execute-plan/SKILL.md index 04bb26b..511de8f 100644 --- a/skills/orch-execute-plan/SKILL.md +++ b/skills/orch-execute-plan/SKILL.md @@ -47,7 +47,7 @@ python3 scripts/orch.py validate-executor-result --task --handoff None: + task = { + "task_id": "task-006", "plan_id": "plan-001", "goal": "Review product behavior", + "requirements": ["REQ-REV-004"], "constraints": [], + "truth_basis": {"purpose": "product review"}, + "semantic_authority": {"requirements": ["REQ-REV-004"]}, + "evidence_capability": {"mode": "direct"}, + "files": {"read": [], "write": ["src/a.py"]}, "interfaces": {}, + "allocated_rules": [], + "methodology": {"primary": "dev-test-driven-development", "skills": []}, + } + candidate = execution_context.build_product_review_candidate( + task=task, base="a" * 40, head="b" * 40, + diff="diff --git a/src/a.py b/src/a.py\n", changed_files=["M\tsrc/a.py"], + changed_symbols=["run"], + validation_observations=[{"id": "VAL-006", "result": "passed"}], + knowledge_disposition={"status": "none", "reason": "task local"}, + ) + encoded = json.dumps(candidate, sort_keys=True) + assert all(term not in encoded for term in ("handoff", "acceptance_review", "reviewer_run")) + assert candidate["task_authority"]["task_id"] == "task-006" + + def _load_orchestration_dispatcher(): path = ORCHESTRATION / "dispatcher.py" spec = importlib.util.spec_from_file_location( diff --git a/tests/test_orchestration_plan_return.py b/tests/test_orchestration_plan_return.py index 4a3316f..0eacdcb 100644 --- a/tests/test_orchestration_plan_return.py +++ b/tests/test_orchestration_plan_return.py @@ -20,7 +20,7 @@ ReviewContractError, classify_first_broken_owner, resume_plan_return, - route_review_verdict, + _route_review_finding as route_review_verdict, ) from stage_events import StageEventError # noqa: E402 diff --git a/tests/test_orchestration_planning_scenarios.py b/tests/test_orchestration_planning_scenarios.py index 1b64c31..3dd9652 100644 --- a/tests/test_orchestration_planning_scenarios.py +++ b/tests/test_orchestration_planning_scenarios.py @@ -17,7 +17,7 @@ ReviewContractError, plan_review_identity, resume_plan_return, - route_review_verdict, + _route_review_finding as route_review_verdict, ) import execution_context # noqa: E402 from test_orchestration_accepted_result import _binding, _handoff, _task, _validated # noqa: E402 diff --git a/tests/test_orchestration_review_frontier.py b/tests/test_orchestration_review_frontier.py index 2a2dcf0..3d720b9 100644 --- a/tests/test_orchestration_review_frontier.py +++ b/tests/test_orchestration_review_frontier.py @@ -258,7 +258,7 @@ def test_rf_03_capable_untouched_invariant_stays_blocking_during_narrow_repair( }] narrow.update(verdict="repair", findings=[safety]) assert review_runtime.validate_review_sequence(narrow, previous_review=prior).verdict == "repair" - routed = review_runtime.route_review_verdict(safety) + routed = review_runtime._route_review_finding(safety) assert routed["first_broken_artifact"] == first_broken assert routed["preserve_valid_work_and_evidence"] is True @@ -309,4 +309,4 @@ def test_rf_06_final_broad_integrated_review_rediscovers_and_classifies_latent_b "observation": "Final broad review rediscovered deferred non-load-bearing B.", }] assert review_runtime.validate_review_finding(latent_finding).finding_id == "RF-LATENT-B" - assert review_runtime.route_review_verdict(latent_finding)["return_to"] == "backlog_owner" + assert review_runtime._route_review_finding(latent_finding)["return_to"] == "backlog_owner" diff --git a/tests/test_orchestration_reviews.py b/tests/test_orchestration_reviews.py index e191238..47f15fc 100644 --- a/tests/test_orchestration_reviews.py +++ b/tests/test_orchestration_reviews.py @@ -13,12 +13,15 @@ REPO_ROOT = Path(__file__).resolve().parents[1] ORCHESTRATION = REPO_ROOT / "scripts" / "orchestration" sys.path.insert(0, str(ORCHESTRATION)) +import review_runtime # noqa: E402 from review_runtime import ( # noqa: E402 ReviewContractError, classify_first_broken_owner, + publish_review, review_evidence_identity, - route_review_verdict, + route_stored_review_verdict, + _route_review_finding as route_review_verdict, transition_review_finding, validate_contract_instance, validate_review_sequence, @@ -266,6 +269,48 @@ def test_api_001_routes_every_class_to_first_broken_owner( assert route_review_verdict(record, **route_context)["return_to"] == expected[1] +def test_review_store_is_required_before_public_finding_routing( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + record = stage_review("integrated_implementation") + record.update( + review_id="review-task-publication", + review_mode="initial", + review_target_kind="stage", + repair_frontier=None, + review_reset=None, + verdict="repair", + ) + item = finding() + item["target_identity"] = record["target_identity"] + record["findings"] = [item] + record["reviewer_run"] = { + "run_id": "reviewer-run-00000000-0000-0000-0000-000000000001", + "sha256": ZERO_SHA, + } + monkeypatch.setattr(review_runtime, "_validate_reviewer_run", lambda *_: None) + + with pytest.raises(ReviewContractError, match="stored review"): + route_stored_review_verdict( + tmp_path, item, current_target_identity=record["target_identity"] + ) + with pytest.raises(ReviewContractError, match="stored review"): + review_runtime.route_review_verdict( + tmp_path, item, current_target_identity=record["target_identity"] + ) + + reference = publish_review( + tmp_path, record, current_target_identity=record["target_identity"] + ) + routed = route_stored_review_verdict( + tmp_path, + reference, + current_target_identity=record["target_identity"], + finding_id=item["finding_id"], + ) + assert routed["return_to"] == "task_owner" + + @pytest.mark.parametrize("field", ["capabilities", "unavailable_evidence"]) def test_api_002_rejects_empty_evidence_strings(field: str) -> None: review = stage_review("specification") diff --git a/tests/test_reviewer_workspace.py b/tests/test_reviewer_workspace.py index 18bbf73..562284f 100644 --- a/tests/test_reviewer_workspace.py +++ b/tests/test_reviewer_workspace.py @@ -6,6 +6,7 @@ from pathlib import Path import subprocess import sys +from unittest.mock import patch import pytest @@ -115,6 +116,76 @@ def test_workspace_contains_copied_direct_evidence_and_declares_network_denied( assert "control_root" not in json.dumps(state) +def test_task_review_worker_output_receives_native_bound_receipt( + review_roots: tuple[Path, Path, Path] +) -> None: + source, control, runtime = review_roots + subprocess.run(["git", "init", "-q", str(source)], check=True) + subprocess.run(["git", "-C", str(source), "add", "src/target.py", ".wor105-review-sentinel"], check=True) + subprocess.run( + ["git", "-C", str(source), "-c", "user.name=Test", "-c", "user.email=test@example.invalid", "commit", "-qm", "fixture"], + check=True, + ) + head = subprocess.check_output(["git", "-C", str(source), "rev-parse", "HEAD"], text=True).strip() + tree = subprocess.check_output(["git", "-C", str(source), "rev-parse", "HEAD^{tree}"], text=True).strip() + identity = {"artifact_id": "task-006", "revision": head, "sha256": "1" * 64, "source_tree": tree} + context = { + "target_identity": identity, + "agent_id": "reviewer-task", + "capability": "judgment", + "execution_id": "review-execution-task", + "evidence_mode": "reproducible_snapshot", + "review_mode": "initial", + "review_target_kind": "task", + "repair_frontier": None, + "review_reset": None, + } + direct = build_direct_evidence_packet( + source_root=source, + control_root=control, + protected_roots=[control / "credentials"], + artifacts=["source:src/target.py"], + search_roots=[], validators=[], sentinels=[], network_state="denied", + task_review_context=context, + ) + created = create_reviewer_workspace(runtime, "review-task-native", direct) + review = { + "required": True, + "reviewer_independent": True, + "review_id": "review-task-native", + "reviewed_head": head, + "review_mode": "initial", + "review_target_kind": "task", + "repair_frontier": None, + "review_reset": None, + "target_identity": identity, + "reviewer": { + "agent_id": "reviewer-task", "capability": "judgment", + "authorship": "none", "repair_participation": "none", + "decision_participation": "none", "deliberation_participation": "none", + "context_origin": "reproducible_snapshot", + }, + "evidence": { + "mode": "reproducible_snapshot", "capabilities": ["frozen source"], + "unavailable_evidence": [], "commands": [], + "artifacts": [{"path": direct["artifacts"][0]["locator"], "sha256": direct["artifacts"][0]["sha256"]}], + }, + "verdict": "accept", "findings": [], + "started_at": "2026-09-08T00:00:00Z", "completed_at": "2026-09-08T00:01:00Z", + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + with patch.object( + reviewer_workspace, + "_run_sandboxed_process", + return_value=subprocess.CompletedProcess(["reviewer"], 0, json.dumps(review), ""), + ): + receipt = reviewer_workspace.run_sandboxed_reviewer(Path(str(created["workspace_path"])), ["reviewer"]) + + assert receipt["status"] == "passed" + assert receipt["task_review_context"]["target_identity"] == identity + assert set(receipt["reviewer_run"]) == {"run_id", "sha256"} + + def test_bounded_read_search_and_validators_are_allowed(review_roots: tuple[Path, Path, Path]) -> None: source, control, runtime = review_roots created = create_reviewer_workspace(runtime, "review-002", packet(source, control)) From cac3dff6139a3fd74abef702bfd85027f56e7796 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Wed, 9 Sep 2026 00:10:21 +0800 Subject: [PATCH 47/48] fix(orchestration): align review preparation and accepted-result reentry Keep controller bookkeeping out of product judgment. Preflight integrated evidence from compact acceptance, compose native review envelopes in the controller, and consume claim-bound observations without executor or validation replay. Reconcile existing workflow owners and cover the lifecycle boundaries with generic regressions. Refs WOR-112. --- .../assets/orchestration/contract/task-v1.md | 6 +- references/assets/orchestration/workflow.md | 47 ++-- references/evals/development/evals.json | 15 ++ rules/orchestration/orch-handoff-required.md | 4 +- .../orch-orchestration-boundary.md | 3 +- rules/orchestration/orch-review-completion.md | 8 +- scripts/orchestration/dispatcher.py | 3 +- scripts/orchestration/execution_context.py | 234 ++++++++++++------ scripts/orchestration/review_runtime.py | 137 ++++++++-- scripts/work-bundle/reviewer_workspace.py | 137 +++++++++- skills/dev-code-review/SKILL.md | 44 ++-- skills/orch-create-handoff/SKILL.md | 2 +- skills/orch-execute-plan/SKILL.md | 8 +- skills/orch-review-plan/SKILL.md | 10 +- tests/test_dev_skill_contracts.py | 56 +++-- tests/test_orchestration_accepted_result.py | 45 +++- tests/test_orchestration_execution_context.py | 129 ++++++++-- tests/test_orchestration_reviews.py | 82 +++++- tests/test_reviewer_workspace.py | 142 +++++++++-- 19 files changed, 893 insertions(+), 219 deletions(-) diff --git a/references/assets/orchestration/contract/task-v1.md b/references/assets/orchestration/contract/task-v1.md index 659330c..0276069 100644 --- a/references/assets/orchestration/contract/task-v1.md +++ b/references/assets/orchestration/contract/task-v1.md @@ -133,7 +133,7 @@ Only `mode` is needed for deterministic checks: its default freshness is 3600 se The identity covers conservative material repository content and index state (including dirty/untracked and declared task-created inputs), semantic validation fields, runner/oracle code, declared dependency/profile identity, OS/architecture/runtime, explicitly relevant environment variables, and the execution binding/cwd. Use `include_head: true` for exact-commit claims such as release validation. Restoring the complete deterministic identity A → B → A may reuse its original fresh result; explicit provenance revocation still invalidates it. Local observations never substitute for GitHub platform evidence through an inferred equivalence. -Generated WorkBundle runtime, handoff, review, and log artifacts are packaging, not implicit source inputs. Other observation outputs require exact repository-relative `output_paths`; explicit read/dependency inputs cannot also be output-only. This affects fingerprinting only: write-scope, Git-neutrality, handoff/task/plan identity, result shape, knowledge disposition, evidence closure, and authorization are still checked on every call. If an expensive check consumes an otherwise excluded artifact, declare it in `dependency_files`. Unknown external dependencies, unsupported links/submodules, or protected source inputs must not be approximated for reuse. Pin execution profiles and declare all relevant environment/dependency inputs; use fresh execution when coverage is uncertain. No per-feature dependency inference is performed. +Generated WorkBundle runtime, handoff, review, and log artifacts are packaging, not implicit source inputs. Other observation outputs require exact repository-relative `output_paths`; explicit read/dependency inputs cannot also be output-only. This affects fingerprinting only. Initial result acceptance checks write scope, Git neutrality, handoff/task/plan identity, result shape, knowledge disposition, evidence closure, and authorization once, then persists compact accepted authority. Post-acceptance continuation revalidates compact authority and current claim-relevant observations without replaying the handoff. If an expensive check consumes an otherwise excluded artifact, declare it in `dependency_files`. Unknown external dependencies, unsupported links/submodules, or protected source inputs must not be approximated for reuse. Pin execution profiles and declare all relevant environment/dependency inputs; use fresh execution when coverage is uncertain. No per-feature dependency inference is performed. A legacy 3-column `Command or inspection | Proves | Expected` row without YAML `kind` is `legacy-untyped`. It fails closed until ordinary artifact repair migrates it to front-matter `kind: process|inspection`. Do not default it to `process`. Never shell-execute ambiguous legacy text. @@ -142,7 +142,7 @@ A legacy 3-column `Command or inspection | Proves | Expected` row without YAML ` - Implementation criteria are satisfied. - Fresh task validation evidence exists. - The compiled task brief carries the accepted Truth Basis. When review is required, the review package carries the same values. -- A valid `executor-result-v1` handoff exists. +- For initial acceptance, a valid `executor-result-v1` handoff exists. Accepted-task repair, publication retry, finalization, and resume consume compact accepted authority and do not require another handoff. - Shared completion validation has passed: task/plan identity, executor-result shape, fresh required validation, `knowledge_disposition`, and unresolved/blocker state. - When `acceptance_review.required` is false or omitted, `Completed` does not require an independent reviewer or `accept`. - When `acceptance_review.required` is true, `acceptance_review.verdict` is `accept`. @@ -151,7 +151,7 @@ A legacy 3-column `Command or inspection | Proves | Expected` row without YAML ` - Both the current and previous task-review records retain `required: true`, `reviewer_independent: true`, and `review_target_kind: task`; the adapter does not infer or overwrite those ownership facts. The accepted repaired target names the completed task and its `source_tree` plus `reviewed_head` must equal the helper-observed Git tree and head. - Material redesign or changed authority, scope, acceptance, decomposition, or validation allocation requires a fresh `initial` review with `review_reset` bound to the prior review, classified reason, and current target and evidence. The reviewer may reuse the same agent identity when judgment-capable and independent by authorship/repair/decision/deliberation participation and review provenance; identity rotation is not a freshness requirement. - Task and stage review results are first-class review-store records. Lifecycle admission takes only `{review_id, sha256}` plus the expected current target; it revalidates the native immutable reviewer-run receipt before exposing a verdict or selecting a finding. Bare output, receipt, or finding objects are non-authoritative. -- The task-review product candidate contains compiled task authority, exact source/diff identity, harness-owned validation observations, unresolved product concerns, and task-local disposition. Executor-handoff structure and publication/status/archive bookkeeping remain controller-owned and are excluded from reviewer judgment. +- The task-review product candidate contains accepted product requirements/boundaries, exact product source/diff identity, normalized harness-owned validation observations, and unresolved product concerns. Handoff, knowledge disposition, reviewer history, and publication/status/archive bookkeeping remain controller-owned and are excluded from reviewer judgment. Controller/orchestration code remains product when allocated by the task. - A stored post-execution task repair review may recompute the compact accepted result while preserving its executor-result digest, validation evidence identities, owner, baseline, and knowledge disposition. This path performs no executor redispatch, replacement handoff, validation rerun, or review-history embedding. ## Planning verification diff --git a/references/assets/orchestration/workflow.md b/references/assets/orchestration/workflow.md index 2af8bf6..24c1a62 100644 --- a/references/assets/orchestration/workflow.md +++ b/references/assets/orchestration/workflow.md @@ -129,10 +129,10 @@ and semantic authority identities. Its closure is derived from the current artif - Plan: the root, every phase/task declaring its plan ID, and every linked verified specification (including member-specific links and their required authority). - Integrated implementation: the same authority closure, the complete clean Git source - tree, and executor handoffs containing evidence for each declared validation command - or inspection ID. The existing native completion-provenance file is included when - present. This is evidence availability, not a replacement validation-result verdict; - existing freshness, binding, authorization, and platform acceptance gates still apply. + tree, and each validation-bearing member's current compact accepted result from its + execution binding. Include its stored native task review when present; an older accepted + representation remains valid without format migration. Historical executor handoffs are + never scanned. The existing completion-provenance file is included when present. For integrated snapshots, Git file modes/blob IDs reconstruct the exact target tree; copied bytes are checked against those blobs before workspace creation. Symlinks, @@ -145,10 +145,12 @@ stage membership and verifies the complete source-tree identity. Removing entrie recomputing packet/receipt hashes cannot turn partial evidence into complete evidence. `stage_target_identity` computes the target from current source artifacts, and -workspace creation checks it again. Run the worker with `reviewer-process-run` using -that runtime root. Its stdout must be exactly one stage-review JSON object, without -`reviewer_run`; the native publisher verifies it against the frozen context and -binds its canonical digest into the receipt. The controller then attaches the run +workspace creation checks it again. Complete stage evidence is checked before any +reviewer process launch. Run the worker with `reviewer-process-run` using that runtime +root. Specification and plan workers retain the stage-review contract; task and +integrated-implementation product workers return the compact `task_review` judgment +defined by `dev-code-review`. The controller constructs the native envelope from frozen target, independence, and evidence context, +then binds its canonical digest into the receipt. The controller then attaches the run ID and SHA-256 of the immutable receipt bytes to that exact result and publishes the task-or-stage envelope as a read-only review-store record. Verdict admission and named-finding routing resolve only that stored reference and recheck its receipt and @@ -177,11 +179,12 @@ remain single-flight. Publication rejects an intervening mutation epoch or expir freshness. Reservation lock files are retained to avoid splitting concurrent waiters; they are runtime artifacts, not source inputs or a separate cache subsystem. -Task code review consumes one product candidate compiled from task authority, exact -source/diff identity, harness-owned validation observations, unresolved product -concerns, and task-local disposition. Executor-handoff schema and publication, -status, and archive bookkeeping remain controller preconditions and never become -product findings. +Task code review consumes one product candidate compiled from accepted product +requirements/boundaries, exact product source/diff identity, normalized harness +observations, and unresolved product concerns. Handoff, knowledge, reviewer-history, +receipt/publication, status, and archive bookkeeping remain controller inputs and do +not enter product judgment. Controller/orchestration code is product when allocated +by the accepted task. The **acceptance once** lifecycle rule makes the harness strongly verify binding, source/scope, subagent ownership, validation, and required review, then persists one @@ -218,15 +221,21 @@ scheduler selects executable task -> Completed ``` -Subagent executors own every implementation and repair mutation, task-local verification, and executor-result evidence, including a task-local knowledge disposition of `none`, `update`, `supersede`, or `reclassify`. They never invoke persistence or read knowledge. Reviewers own acceptance judgment for the accepted Truth Basis, requirement fit, correctness, edge cases, test oracle, disposition, unnecessary complexity, allocated obligations, and validation sufficiency. Schedulers own dependencies, barriers, context compilation, neutral subagent binding, validation routing, and evidence shape; they do not perform code-quality review or mutate task write scope. +Subagent executors own every implementation and repair mutation, task-local verification, and executor-result evidence, including a task-local knowledge disposition of `none`, `update`, `supersede`, or `reclassify`. They never invoke persistence or read knowledge. Product reviewers judge accepted product requirements/boundaries, exact source/diff, correctness, edge cases, normalized validation observations, unresolved product concerns, and unnecessary complexity. Controllers own disposition, handoff, provenance, publication, and lifecycle mechanics. Schedulers own dependencies, barriers, context compilation, neutral subagent binding, validation routing, and evidence shape; they do not perform code-quality review or mutate task write scope. Selecting `orch-execute-plan` requires a subagent owner for every task without a separate user opt-in. The production `TaskOwnershipScheduler` admission entry consumes either a host-native or Execution-Flow adapter; evidence records only the minimum agent/run identity and mechanism. If none is available, execution fails closed before task mutation. Independent disjoint tasks in distinct execution workspaces dispatch before any wait; dependent, overlapping, or same-workspace tasks serialize. Acceptance uses the same scheduler entry to reject controller mutation, and repair dispatch uses `operation: repair` through the same adapter path. -On `repair`, return blocking findings with the same brief and current diff to the task-owning subagent. It makes the smallest repair, reruns claim-relevant validation, regenerates the package from the original base, and reviews again. If no subagent is available, fail closed before repair mutation. After two failed low-cost repair rounds, escalate the capability tier; if evidence indicates a plan or specification defect, stop the retry loop and route the typed blocker. - -On reviewer infrastructure or provider failure, replace only the reviewer against the -same immutable review package; source identity, validation evidence, plan decomposition, -and review frontier remain unchanged. A finding-scoped repair under unchanged authority +On `repair`, return blocking findings to the existing task owner, repair from the exact +previously reviewed source, rerun only claim-relevant invalidated validation, and +perform one scoped rereview. Preserve unaffected accepted executor/validation authority. +Initial acceptance uses one executor-result handoff; accepted-task source repair consumes +the compact accepted result; publication-only/control resume reuses the completed +judgment and never redispatches, rewrites a handoff, or reruns validation/review. + +On reviewer infrastructure or provider failure, repair the first broken runner/provider +against the same immutable review package; a capable independent reviewer may be reused, +and completed judgment publication is idempotent. Source identity, validation evidence, +plan decomposition, and review frontier remain unchanged. A finding-scoped repair under unchanged authority carries the previous finding/evidence frontier and reviews only repaired boundaries. Only a material authority, scope, acceptance, decomposition, or validation-allocation change resets review to an initial frontier. diff --git a/references/evals/development/evals.json b/references/evals/development/evals.json index ff24e13..d1e3042 100644 --- a/references/evals/development/evals.json +++ b/references/evals/development/evals.json @@ -104,6 +104,21 @@ "id": "dev-lightweight-amendment-lane-separation", "prompt": "A pre-mutation one-file amendment remains same-owner and mechanically bounded, but the agent proposes adding an executor result, task state, review package, and archive record for assurance.", "expected_output": "Allows only the exact bounded Files.Modify amendment and rejects heavy lifecycle artifacts; the lightweight lane remains one disposable plan." + }, + { + "id": "dev-review-product-defect", + "prompt": "Review an exact frozen product diff against REQ-17. Current normalized tests pass, but a boundary case demonstrably contradicts REQ-17.", + "expected_output": "Returns repair with a compact stable finding that names REQ-17, the affected product boundary and evidence, expected versus observed behavior, and owner task_owner." + }, + { + "id": "dev-review-control-input-failure", + "prompt": "The product candidate is unavailable because its publication receipt is malformed, while no product source or validation observation can be inspected.", + "expected_output": "Returns an input/runner failure outside the product verdict, creates no product finding from the receipt defect, and does not rerun validation or confirm another review." + }, + { + "id": "dev-review-adversarial-non-trigger", + "prompt": "Audit whether a completed task handoff is indexed and whether archive status may advance; no product implementation judgment is requested.", + "expected_output": "Treats this as controller/final-audit work and does not invoke dev-code-review or emit a product verdict." } ] } diff --git a/rules/orchestration/orch-handoff-required.md b/rules/orchestration/orch-handoff-required.md index 2330a08..fb7e475 100644 --- a/rules/orchestration/orch-handoff-required.md +++ b/rules/orchestration/orch-handoff-required.md @@ -12,7 +12,7 @@ requires: [] ## Purpose -Require compact executor-result handoffs before reporting execution complete or blocked. Handoffs record only continuation and review evidence for the next agent; durable knowledge and orchestration strategy decisions stay outside executor-result handoffs. +Require one compact executor-result handoff for an initial executor result before its first acceptance. After acceptance, continuation consumes the compact accepted result; acquiring, publishing, or retrying review/control facts does not rewrite or replay an executor handoff. Durable knowledge and orchestration strategy decisions stay outside executor-result handoffs. ## Must @@ -71,4 +71,4 @@ Require compact executor-result handoffs before reporting execution complete or ## On Violation -Stop completion reporting, create or repair the missing compact executor-result handoff, remove forbidden advice fields, add missing task-fit, CodeGraph, repository, validation, contract-decoupling, barrier, convergence, violation-closure, or `delegation_evidence`, update indexes and statuses from the handoff evidence when supported, and only then resume the next executable action or review step. If active orchestration handoff creation is attempted, reject it and use active specs, plans, phases, tasks, indexes, and executor-result handoffs for continuation state. +For an unaccepted initial executor result, stop completion reporting and create or repair its compact executor-result handoff. For an already accepted task, use its compact accepted result and route only the affected product, publication, or finalization owner; never redispatch execution merely because a handoff is absent from post-acceptance context. Remove forbidden advice fields and fill missing initial-result evidence only within that initial handoff. If active orchestration handoff creation is attempted, reject it and use active specs, plans, phases, tasks, indexes, and compact accepted results for continuation state. diff --git a/rules/orchestration/orch-orchestration-boundary.md b/rules/orchestration/orch-orchestration-boundary.md index 0ea2dab..7c72c2d 100644 --- a/rules/orchestration/orch-orchestration-boundary.md +++ b/rules/orchestration/orch-orchestration-boundary.md @@ -31,7 +31,8 @@ Orchestration artifacts are derived working material under `.work-bundle/orchest | **Phase** | Bounded milestone grouping related tasks with only the spec IDs, decisions, files, and tests those tasks need | | **Task** | One executable unit with exact source files, target files, symbols, steps, validation, completion criteria, and handoff requirements | | **Handoff** | Executor or orchestration continuation evidence before advancing status | -| **Review** | Final verification, repair-spec creation on failure, and archival on success | +| **Product review** | Independent judgment of one frozen product candidate against accepted product requirements and normalized observations | +| **Controller finalization** | Evidence admission, first-owner routing, lifecycle completion, and archive mechanics | - Reference spec IDs in downstream plans, phases, and tasks instead of duplicating full requirement prose. - Carry only task-specific execution detail in task files after citing stable spec IDs. diff --git a/rules/orchestration/orch-review-completion.md b/rules/orchestration/orch-review-completion.md index e8aa36d..10a4508 100644 --- a/rules/orchestration/orch-review-completion.md +++ b/rules/orchestration/orch-review-completion.md @@ -16,10 +16,10 @@ Keep final review focused on whether the WorkBundle workflow completed correctly ## Must -- Confirm each required task review compared the accepted Truth Basis, implementation, test oracle, and task-local knowledge disposition before accepting the task. +- Confirm each required task review judged the accepted product requirements/boundaries, exact product source/diff, normalized validation observations, and unresolved product concerns before accepting the task. - Admit a task-or-stage verdict or route a finding only from its immutable review-store reference after native reviewer-run receipt and exact current-target validation. Treat bare reviewer output, unattached receipts, and bare findings as observations only. -- Keep product review candidates limited to task authority, exact source/diff identity, harness-owned observations, unresolved product concerns, and task-local disposition. Handoff schema and publication/archive bookkeeping remain controller audit concerns and cannot become product findings. -- On reviewer infrastructure or provider failure, replace only the reviewer against the same immutable review package; preserve source identity, validation evidence, plan decomposition, and review frontier. +- Keep product review candidates limited to accepted product requirements/boundaries, exact source/diff identity, harness-owned normalized observations, and unresolved product concerns. Handoff, knowledge, reviewer-history, and publication/archive bookkeeping remain controller audit concerns and cannot become review inputs. Controller/orchestration code remains reviewable product when allocated by the task. +- On reviewer infrastructure or provider failure, preserve the immutable candidate and repair the first broken preparation/provider owner. A still-independent capable reviewer may be reused; infrastructure failure does not itself require identity rotation, source change, validation rerun, or another product review after a completed judgment. - For a finding-scoped repair review under unchanged authority, carry exactly the previous finding/evidence frontier and review only the repaired identity and affected boundaries. Reset to an initial frontier only after a material authority, scope, acceptance, decomposition, or validation-allocation change. - Check that declared completion evidence corresponds to the compiled Truth Basis, source IDs, expected delta, and remaining AUTH constraints. - Before archive or completion, confirm every accepted validation-bearing invariant has a compiled `evidence_capability` entry and capable, current, correctly bounded harness-observed evidence under its allocated INV/VAL identities. Treat incapable green, contradiction, staleness, wrong-boundary, failure, missing, or unexecuted evidence as negative acceptance evidence, not closure. @@ -34,7 +34,7 @@ Keep final review focused on whether the WorkBundle workflow completed correctly - Verify declared dependency, barrier, and convergence gates from recorded evidence. - Use declared plan-level/integration acceptance from recorded validation; do not start another implementation-review agent to produce plan-level acceptance. - Aggregate only accepted task dispositions. Any accepted `update`, `supersede`, or `reclassify` promotes final durable closure to `required` even when the upstream specification says `not-needed`; accepted `none` and rejected dispositions do not trigger closure. -- Route missing handoff, status, validation, or review evidence to `review-blocked` and resume the owning execution step. +- Route missing evidence to its first owner: an initial executor result may require its handoff; an accepted-task source repair resumes the existing task owner with claim-relevant validation and a scoped rereview; publication-only/control resume uses the compact accepted result and retries control publication/finalization without executor redispatch, handoff rewrite, validation rerun, or review rerun. - Route incomplete durable knowledge work to `knowledge-blocked` and resume the approved `ks-*` delegate-return path. - Route incomplete repository metadata, index, workspace, or archive mechanics to `repository-blocked` or `workspace-blocked` and use bounded deterministic helpers. - Require the execution-evidence-driven final Knowledge Base Update disposition to be `completed` or `not-needed` before archive; archive remains blocked while promoted closure lacks validated keep-summarizing return evidence. diff --git a/scripts/orchestration/dispatcher.py b/scripts/orchestration/dispatcher.py index 8d665da..f416d5a 100644 --- a/scripts/orchestration/dispatcher.py +++ b/scripts/orchestration/dispatcher.py @@ -76,9 +76,10 @@ def build_parser() -> argparse.ArgumentParser: task_brief.set_defaults(func=cmd_build_task_brief) review_package = sub.add_parser("build-review-package", parents=[parent]) review_package.add_argument("--task", required=True) - review_package.add_argument("--handoff", required=True) + review_package.add_argument("--handoff") review_package.add_argument("--base", required=True) review_package.add_argument("--head", required=True) + review_package.add_argument("--validation-observation-id", action="append", default=[]) _add_acceptance_runtime_inputs(review_package) review_package.set_defaults(func=cmd_build_review_package) validate_result = sub.add_parser("validate-executor-result", parents=[parent]) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index af93a85..69b12a9 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -932,6 +932,9 @@ def project_validation_evidence( observation = observed_by_id.get(evidence_id, {}) projected.append({ "id": evidence_id, + "command": item.get("command"), + "invariant_ids": list(_as_list(item.get("invariant_ids"))), + "observation_id": observation.get("observation_id"), "digest": semantic_digest({"command": item.get("command"), "result": result}), "result": result, "boundary": invariant.get("boundary", "component"), @@ -1822,6 +1825,69 @@ def _load_materialized_accepted_task_result( return binding, dict(prior) +def _claim_bound_validation_observations( + binding: Mapping[str, Any], + task: Mapping[str, Any], + evidence: Mapping[str, Any], + observation_ids: Sequence[str], +) -> list[dict[str, Any]]: + """Resolve existing observations against current validation claim identities without replay.""" + + ids = list(observation_ids) + validation_items = [item for item in _as_list(task.get("validation")) if isinstance(item, Mapping)] + if ( + len(ids) != len(set(ids)) + or len(ids) != len(validation_items) + or any(not isinstance(item, str) or not item for item in ids) + ): + raise SystemExit("validation evidence is missing, duplicate, or extra") + + class _ObservationUnavailable(RuntimeError): + pass + + def no_validation_replay(_: dict[str, Any]) -> dict[str, Any]: + raise _ObservationUnavailable + + execution_path = Path(str(binding.get("execution_path") or "")).expanduser().resolve() + store = _completion_provenance_module().ManagedProvenanceStore( + Path(str(binding.get("control_root") or task.get("workspace", {}).get("root") or "")) + / ".work-bundle/runtime/completion-provenance" + ) + matched: list[dict[str, Any]] = [] + for position, item in enumerate(validation_items, start=1): + try: + observation = _completion_provenance_module().observe_validation( + binding, + task, + item, + evidence, + no_validation_replay, + lambda: capture_repository_evidence(execution_path), + ) + record = _completion_provenance_module().load_observation( + store, str(observation.get("observation_id") or "") + ).to_dict() + except (_ObservationUnavailable, _completion_provenance_module().CompletionProvenanceError) as error: + raise SystemExit( + "an existing current claim-bound validation observation is required" + ) from error + if record["result"]["exit_code"] != 0: + raise SystemExit("validation evidence is not a passing observation") + matched.append( + { + "id": item.get("id") or f"validation-{position:03d}", + "command": item.get("command"), + "invariant_ids": list(_as_list(item.get("invariant_ids"))), + "observation_id": observation["observation_id"], + "result": "passed", + "product_tree": record["product_tree"], + } + ) + if set(ids) != {item["observation_id"] for item in matched}: + raise SystemExit("validation evidence does not bind current task claims") + return matched + + def materialize_accepted_task_review( control_root: Path, task: Mapping[str, Any], @@ -1829,6 +1895,7 @@ def materialize_accepted_task_review( causal_classification: Mapping[str, Any], *, accepted_at: str | None = None, + validation_evidence_ids: Sequence[str] | None = None, ) -> dict[str, Any]: """Compose prior executor authority with one standalone current review.""" @@ -1951,6 +2018,22 @@ def materialize_accepted_task_review( if ancestor.returncode != 0: raise SystemExit("accepted task repair review target is not an ancestor of current HEAD") + prior_validation_digest = prior["authority_projection"]["validation_obligations_digest"] + current_validation_digest = semantic_digest(_accepted_validation_projection(task)) + accepted_source_changed = reviewed_head != prior["accepted_source"]["head"] + if validation_evidence_ids is None and not accepted_source_changed and ( + prior_validation_digest == current_validation_digest + ): + current_validation_ids = list(prior["validation_evidence_ids"]) + else: + matched_validation = _claim_bound_validation_observations( + binding, + task, + evidence, + validation_evidence_ids or prior["validation_evidence_ids"], + ) + current_validation_ids = sorted(item["observation_id"] for item in matched_validation) + authority_projection = _accepted_authority_projection( task, binding, accepted_review=review, owner_identity=prior["owner_identity"] ) @@ -1974,6 +2057,7 @@ def materialize_accepted_task_review( authority_projection=authority_projection, review_id=validated_review.review_id, accepted_at=accepted_at or datetime.now(timezone.utc).isoformat().replace("+00:00", "Z"), + validation_evidence_ids=current_validation_ids, ) updated = dict(binding) updated["accepted_result"] = accepted @@ -1987,6 +2071,7 @@ def materialize_accepted_task_repair_review( review: Mapping[str, Any], *, accepted_at: str | None = None, + validation_evidence_ids: Sequence[str] | None = None, ) -> dict[str, Any]: """Compatibility wrapper for unchanged-authority standalone task repair review.""" @@ -2000,6 +2085,7 @@ def materialize_accepted_task_repair_review( "authorized_lifecycle_action": "rematerialize_accepted_result", }, accepted_at=accepted_at, + validation_evidence_ids=validation_evidence_ids, ) @@ -3796,6 +3882,7 @@ def validate_executor_result_for_task( prior_ownership: Mapping[str, Mapping[str, object]] | None = None, repair_continuity: Mapping[str, Mapping[str, object] | RepairContinuity] | None = None, authorized_replacements: Iterable[str] | None = None, + preparing_review: bool = False, ) -> dict[str, Any]: if handoff.get("type") != "executor-result": raise SystemExit("Handoff is not executor-result") @@ -3828,7 +3915,13 @@ def validate_executor_result_for_task( if state in {"completed", "partial"}: _assert_task_fit_check(handoff, task_id, state) _assert_changed_paths_in_write_scope(handoff, task_files) - acceptance_review_sequence = _assert_handoff_review_matches_task(handoff, task, state) + if preparing_review: + review = handoff.get("acceptance_review") if isinstance(handoff.get("acceptance_review"), dict) else {} + if (task.get("review_required") is True) != (review.get("required") is True): + raise SystemExit("Executor result acceptance_review.required must match compiled review_required") + acceptance_review_sequence = None + else: + acceptance_review_sequence = _assert_handoff_review_matches_task(handoff, task, state) required_items = [ item for item in _as_list(task.get("validation")) @@ -4841,7 +4934,7 @@ def build_product_review_candidate( changed_files: Sequence[str], changed_symbols: Sequence[str], validation_observations: Sequence[Mapping[str, Any]], - knowledge_disposition: Mapping[str, Any], + knowledge_disposition: Mapping[str, Any] | None = None, unresolved: Sequence[Any] = (), ) -> dict[str, Any]: """Build the sole semantic task-review input, excluding transport bookkeeping.""" @@ -4853,23 +4946,20 @@ def build_product_review_candidate( "goal": task.get("goal"), "requirements": list(_as_list(task.get("requirements"))), "constraints": list(_as_list(task.get("constraints"))), - "truth_basis": task.get("truth_basis", {}), - "semantic_authority": task.get("semantic_authority", {}), - "evidence_capability": task.get("evidence_capability", {}), + "accepted_boundaries": list(_as_list( + (task.get("truth_basis") or {}).get("decision_authority") + if isinstance(task.get("truth_basis"), Mapping) else [] + )), "files": task.get("files", {}), "interfaces": task.get("interfaces", {}), - "allocated_rules": list(_as_list(task.get("allocated_rules"))), - "methodology": task.get("methodology", {}), }, "source": { "base": base, "head": head, "diff": diff, "changed_files": list(changed_files), - "changed_symbols": list(changed_symbols), }, "validation_observations": [dict(item) for item in validation_observations], - "knowledge_disposition": dict(knowledge_disposition), "unresolved": list(unresolved), } forbidden = {"handoff", "acceptance_review", "publication", "reviewer_run"} @@ -4880,8 +4970,8 @@ def build_product_review_candidate( def build_review_package(args: argparse.Namespace) -> Path: - if not args.handoff or not args.base or not args.head: - raise SystemExit("build-review-package requires --handoff, --base, and --head") + if not args.base or not args.head: + raise SystemExit("build-review-package requires --base and --head") target, brief_document = _compile_task_brief(args) root = resolve_workspace_root(args) task = brief_document["task_brief"] @@ -4889,17 +4979,37 @@ def build_review_package(args: argparse.Namespace) -> Path: plan_id = str(task.get("plan_id") or "") if not plan_id: raise SystemExit(f"Task brief is missing plan_id for {task_id}") - handoff_root = root / ".work-bundle/orchestration/handoff" - handoff_path = _input_path(args.handoff, root, handoff_root, "handoff") - handoff, _ = _read_structured(handoff_path) - validated = validate_executor_result_for_task(handoff, task, observe=True, **_observation_kwargs(args)) - knowledge_disposition = validated["knowledge_disposition"] - review_request = handoff.get("acceptance_review") if isinstance(handoff.get("acceptance_review"), dict) else {} - review_mode = str(review_request.get("review_mode") or "initial") + handoff: dict[str, Any] = {} + validated: dict[str, Any] = {} + accepted: dict[str, Any] | None = None + binding_path = _binding_path(root, plan_id, task_id) + raw_binding = _read_binding_file(binding_path) if binding_path.is_file() else {} + if isinstance(raw_binding.get("accepted_result"), Mapping): + binding = load_task_execution_binding(root, plan_id, task_id) + _, accepted = _load_materialized_accepted_task_result(root, task) + accepted_source = accepted["accepted_source"] + if not isinstance(accepted_source, Mapping): + raise SystemExit("review-blocked: accepted source identity is invalid") + execution_root = Path(str(binding["execution_path"])).resolve() + if _resolve_commit(execution_root, str(args.base)) != accepted_source["head"]: + raise SystemExit("review-blocked: accepted-task repair base must be the accepted source") + review_request = {} + review_mode = "repair" + elif args.handoff: + handoff_root = root / ".work-bundle/orchestration/handoff" + handoff_path = _input_path(args.handoff, root, handoff_root, "handoff") + handoff, _ = _read_structured(handoff_path) + validated = validate_executor_result_for_task( + handoff, task, observe=True, preparing_review=True, **_observation_kwargs(args) + ) + review_request = handoff.get("acceptance_review") if isinstance(handoff.get("acceptance_review"), dict) else {} + review_mode = str(review_request.get("review_mode") or "initial") + else: + raise SystemExit("build-review-package requires an initial executor handoff or accepted task result") if review_mode not in {"initial", "repair"}: raise SystemExit("review-blocked: review_mode must be initial or repair") repair_frontier: dict[str, Any] | None = None - if review_mode == "repair": + if review_mode == "repair" and accepted is None: try: from review_runtime import ReviewContractError, _repair_frontier repair_frontier = dict(_repair_frontier(review_request.get("repair_frontier"))) @@ -4907,7 +5017,8 @@ def build_review_package(args: argparse.Namespace) -> Path: raise SystemExit(f"review-blocked: invalid repair frontier: {error}") from error elif review_request.get("repair_frontier") not in (None, {}): raise SystemExit("review-blocked: initial review cannot carry repair_frontier") - binding = load_task_execution_binding(root, plan_id, task_id) + if accepted is None: + binding = load_task_execution_binding(root, plan_id, task_id) execution_root = Path(str(binding["execution_path"])).resolve() base = _resolve_commit(execution_root, str(args.base)) @@ -4970,13 +5081,32 @@ def build_review_package(args: argparse.Namespace) -> Path: if isinstance(item, dict) } normalized_validation = [] - for position, item in enumerate(validation_commands, start=1): - compiled = compiled_validation.get(str(item.get("command") or ""), {}) - normalized_validation.append({**item, "id": item.get("id") or compiled.get("id") or f"validation-{position:03d}"}) + if accepted is not None: + observation_ids = list(getattr(args, "validation_observation_id", None) or []) + if not observation_ids: + raise SystemExit( + "review-blocked: accepted-task source repair requires explicit current validation observations" + ) + try: + repository_evidence = capture_repository_evidence(execution_root) + except RuntimeError as error: + raise SystemExit(f"review-blocked: repository identity is unavailable: {error}") from error + if repository_evidence.get("head") != head: + raise SystemExit("review-blocked: validation observations require the exact clean review head") + try: + normalized_validation = _claim_bound_validation_observations( + binding, task, repository_evidence, observation_ids + ) + except SystemExit as error: + raise SystemExit(f"review-blocked: {error}") from error + else: + for position, item in enumerate(validation_commands, start=1): + compiled = compiled_validation.get(str(item.get("command") or ""), {}) + normalized_validation.append({**item, "id": item.get("id") or compiled.get("id") or f"validation-{position:03d}"}) evidence_projection = project_validation_evidence( normalized_validation, evidence_capability=task.get("evidence_capability") if isinstance(task.get("evidence_capability"), dict) else {}, - observed=validated.get("observed_validation"), + observed=(normalized_validation if accepted is not None else validated.get("observed_validation")), expansion_reason=("failed_validation" if any(item.get("result") == "failed" for item in normalized_validation) else None), ) unresolved = _as_list(handoff.get("unresolved")) @@ -4985,7 +5115,6 @@ def build_review_package(args: argparse.Namespace) -> Path: "changed_symbols": symbols, "validation": evidence_projection, "unresolved": unresolved, - "knowledge_disposition": knowledge_disposition, } _assert_no_credential_values(evidence, "review evidence") @@ -4997,21 +5126,19 @@ def build_review_package(args: argparse.Namespace) -> Path: changed_files=name_status, changed_symbols=symbols, validation_observations=evidence_projection, - knowledge_disposition=knowledge_disposition, unresolved=unresolved, ) authority = candidate["task_authority"] source = candidate["source"] - required = [f"Goal: {authority.get('goal')}", *authority.get("requirements", []), *authority.get("constraints", [])] + required = [ + f"Goal: {authority.get('goal')}", *authority.get("requirements", []), + *authority.get("constraints", []), *authority.get("accepted_boundaries", []), + ] interfaces = authority.get("interfaces", {}) if isinstance(interfaces, dict): required.extend(_as_list(interfaces.get("consumes"))) required.extend(_as_list(interfaces.get("produces"))) - assertions = [ - *[f"rule {item['id']}: {item['requirement']}" for item in authority.get("allocated_rules", [])], - f"methodology {authority['methodology'].get('primary')}: skills {', '.join(map(str, authority['methodology'].get('skills', []))) or 'none'}", - ] allowed_scope = list(dict.fromkeys([*authority.get("files", {}).get("write", []), *authority.get("files", {}).get("read", [])])) lines = [ "# Task Review Package", @@ -5024,34 +5151,16 @@ def build_review_package(args: argparse.Namespace) -> Path: "## Required behavior", *_markdown_items(required), "", - "## Accepted Truth Basis", - *_markdown_items([authority.get("truth_basis", {})]), - "", - "## Semantic authority", - *_markdown_items([authority.get("semantic_authority", {})]), - "", - "## Evidence capability", - *_markdown_items([authority.get("evidence_capability", {})]), - "", "## Allowed scope", *_markdown_items(allowed_scope), "", "## Changed files", *_markdown_items(source["changed_files"]), "", - "## Changed symbols", - *_markdown_items(source["changed_symbols"]), - "", "## Validation reported", *_markdown_items(candidate["validation_observations"]), "", - "## Knowledge disposition", - *_markdown_items([candidate["knowledge_disposition"]]), - "", - "## Allocated rule and methodology assertions", - *_markdown_items(assertions), - "", - "## Unresolved concerns", + "## Unresolved product concerns", *_markdown_items(candidate["unresolved"]), "", "## Diff", @@ -5059,23 +5168,6 @@ def build_review_package(args: argparse.Namespace) -> Path: source["diff"].rstrip(), "```", ] - if repair_frontier is not None: - lines.extend( - [ - "", - "## Repair frontier", - *_markdown_items( - [{ - "prior_review_id": repair_frontier["prior_review_id"], - "blocking_finding_ids": repair_frontier["blocking_finding_ids"], - "previous_reviewed_identity": repair_frontier["previous_reviewed_identity"], - "repaired_identity": repair_frontier["repaired_identity"], - "affected_boundaries": repair_frontier["affected_boundaries"], - "frozen_evidence_reference": repair_frontier["frozen_evidence_reference"], - }] - ), - ] - ) if out_of_scope: lines.extend( [ @@ -5090,11 +5182,9 @@ def build_review_package(args: argparse.Namespace) -> Path: "## Review rubric", "1. Required behavior is satisfied.", "2. Listed out-of-scope diagnostics are expected sibling or prior changes, not a defect in this task.", - "3. Methodology and allocated-rule obligations are satisfied.", - "4. Accepted purpose, source evidence, decision authority, expected delta, and test oracle agree.", - "5. Knowledge disposition is task-local, evidence-backed, and grants no persistence authority.", - "6. Validation evidence is sufficient and task-scoped.", - "7. Code quality has no blocking defect.", + "3. Accepted product requirements, exact product source/diff, and test oracle agree.", + "4. Validation observations are sufficient and task-scoped.", + "5. Correctness, edge cases, compatibility, and code quality have no blocking defect.", ] ) package = "\n".join(lines).rstrip() + "\n" diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index 05d482b..e4dcd4c 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -157,7 +157,101 @@ def stage_target_identity(root: Path, stage: str, path: Path, *, source_root: Pa return identity -def stage_evidence_requirements(root: Path, stage: str, target: Path) -> tuple[dict[str, str], list[str]]: +_ACCEPTED_RESULT_FIELDS = { + "schema", "plan_id", "task_id", "binding_id", "baseline_identity", + "accepted_source", "authority_projection", "executor_result_digest", + "validation_evidence_ids", "review_id", "owner_identity", "accepted_at", + "invalidation", +} +_ACCEPTED_AUTHORITY_FIELDS = { + "task_digest", "binding_digest", "scope_digest", "validation_obligations_digest", + "required_review_digest", "ownership_digest", +} + + +def accepted_result_state_digest(accepted: Mapping[str, Any]) -> str: + """Recompute the compact accepted-result identity without importing execution runtime.""" + + source = _mapping(accepted.get("accepted_source"), "accepted task result source") + state = { + "plan_id": accepted.get("plan_id"), "task_id": accepted.get("task_id"), + "binding_id": accepted.get("binding_id"), + "baseline_identity": dict(_mapping(accepted.get("baseline_identity"), "accepted baseline")), + "accepted_source": {"head": source.get("head"), "tree": source.get("tree")}, + "authority_projection": dict(_mapping(accepted.get("authority_projection"), "accepted authority")), + } + if "knowledge_disposition" in accepted: + state["knowledge_disposition"] = dict( + _mapping(accepted.get("knowledge_disposition"), "accepted knowledge disposition") + ) + return hashlib.sha256( + json.dumps(state, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode() + ).hexdigest() + + +def _accepted_task_stage_evidence( + root: Path, plan_id: str, task_id: str, *, validate_native_receipt: bool +) -> tuple[Path | None, Path | None, str | None]: + binding = root / ".work-bundle/runtime/execution" / plan_id / task_id / "execution-binding.json" + if binding.is_symlink() or not binding.is_file() or not binding.resolve().is_relative_to(root.resolve()): + return None, None, f"accepted_task_result_missing:{task_id}" + try: + payload = _mapping(json.loads(binding.read_text()), "task execution binding") + accepted = _mapping(payload.get("accepted_result"), "accepted task result") + fields = set(accepted) + if frozenset(fields) not in { + frozenset(_ACCEPTED_RESULT_FIELDS), + frozenset(_ACCEPTED_RESULT_FIELDS | {"knowledge_disposition"}), + }: + raise ReviewContractError("accepted task result shape is not closed") + source = _mapping(accepted.get("accepted_source"), "accepted task result source") + authority = _mapping(accepted.get("authority_projection"), "accepted task result authority") + ownership = _mapping(payload.get("ownership"), "task execution ownership") + observations = accepted.get("validation_evidence_ids") + if ( + accepted.get("schema") != "accepted-task-result-v1" + or accepted.get("plan_id") != plan_id or accepted.get("task_id") != task_id + or accepted.get("binding_id") != ownership.get("binding_id") + or payload.get("plan_id") != plan_id or payload.get("task_id") != task_id + or accepted.get("invalidation") is not None + or set(source) != {"head", "tree", "state_digest"} + or set(authority) != _ACCEPTED_AUTHORITY_FIELDS + or not isinstance(observations, list) or not observations + or any(not isinstance(item, str) or not item for item in observations) + or len(observations) != len(set(observations)) + or source.get("state_digest") != accepted_result_state_digest(accepted) + ): + raise ReviewContractError("accepted task result binding or evidence is invalid") + review_path = None + if accepted.get("review_id"): + review_path = _review_store_path(root, str(accepted["review_id"])) + if review_path.exists(): + if review_path.is_symlink() or review_path.stat().st_mode & 0o222: + raise ReviewContractError("stored current task review is mutable") + review = _mapping(json.loads(review_path.read_text()), "stored current task review") + validated = _validated_review_envelope(review) + if ( + validated.review_id != accepted["review_id"] + or review.get("review_target_kind") != "task" + or validated.verdict != "accepted" + or validated.target_identity.get("artifact_id") != task_id + or validated.target_identity.get("revision") != source.get("head") + or validated.target_identity.get("source_tree") != source.get("tree") + ): + raise ReviewContractError("stored current task review does not bind accepted source") + if validate_native_receipt: + _validate_reviewer_run(root, review) + else: + # Accepted tasks predating native publication remain authoritative. + review_path = None + return binding, review_path, None + except (OSError, ValueError, TypeError, ReviewContractError): + return binding, None, f"accepted_task_result_invalid:{task_id}" + + +def stage_evidence_requirements( + root: Path, stage: str, target: Path, *, validate_native_receipts: bool = True +) -> tuple[dict[str, str], list[str]]: """Derive the stage's evidence closure, not a caller-selected context projection. Carried knowledge constraints are authority in the specification itself. Their @@ -218,31 +312,18 @@ def control(path: Path, role: str) -> None: item, _ = _read_structured(member) if not item.get("validation"): continue - found = False - for handoff in sorted((root / ".work-bundle/orchestration/handoff/executor").rglob("*")): - if handoff.suffix not in {".yaml", ".yml", ".json"} or not handoff.is_file(): - continue - handoff = _input_path(handoff, root, root / ".work-bundle/orchestration/handoff/executor", "stage validation evidence") - value = json.loads(handoff.read_text()) if handoff.suffix == ".json" else _read_structured(handoff)[0] - related = value.get("related", {}) - validation = value.get("validation", {}) - if not isinstance(related, dict) or not isinstance(validation, dict): - continue - records = [record for record in _as_list(validation.get("commands")) if isinstance(record, dict)] - checks = _as_list(item["validation"]) - def covered(check: Any) -> bool: - check = {"command": check} if isinstance(check, str) else check - if not isinstance(check, dict): - return False - command = check.get("command") - check_id = check.get("id") - return any((command and record.get("command") == command) - or (not command and check_id and record.get("id") == check_id) for record in records) - if related.get("plan") == data.get("id") and related.get("task") == item.get("id") and all(covered(check) for check in checks): - control(handoff, "validation_evidence") - found = True - if not found: - missing.append("validation_evidence:" + str(item.get("id"))) + task_id = str(item.get("id") or "") + binding, review, failure = _accepted_task_stage_evidence( + root, str(data.get("id") or ""), task_id, + validate_native_receipt=validate_native_receipts, + ) + if failure: + missing.append(failure) + continue + assert binding is not None + control(binding, "accepted_task_result") + if review is not None: + control(review, "accepted_task_review") # Preserve native identities/receipts; do not invent a parallel validation store. path = root / ".work-bundle/runtime/completion-provenance/completion-provenance-v1.json" if path.is_file(): @@ -351,7 +432,9 @@ def validate_stage_evidence(root: Path, context: Mapping[str, Any], packet: Mapp raise ReviewContractError("repair stage evidence does not bind frozen evidence") required, missing = {target: "target"}, [] else: - required, missing = stage_evidence_requirements(root, str(context["stage"]), root / target[8:]) + required, missing = stage_evidence_requirements( + root, str(context["stage"]), root / target[8:], validate_native_receipts=False + ) source = manifest.get("source_tree", []) if context["stage"] == "integrated_implementation" and context.get("review_mode", "initial") == "initial": if snapshot_tree_identity(source) != context["target_identity"]["source_tree"]: diff --git a/scripts/work-bundle/reviewer_workspace.py b/scripts/work-bundle/reviewer_workspace.py index 6ddd6d3..fcaa2cd 100644 --- a/scripts/work-bundle/reviewer_workspace.py +++ b/scripts/work-bundle/reviewer_workspace.py @@ -76,7 +76,8 @@ def _validate_task_context(context: object) -> dict[str, object]: "target_identity", "agent_id", "capability", "execution_id", "evidence_mode", "review_mode", "review_target_kind", "repair_frontier", "review_reset", } - if not isinstance(context, dict) or set(context) != fields: + allowed = {frozenset(fields), frozenset(fields | {"previous_review"})} + if not isinstance(context, dict) or frozenset(context) not in allowed: raise ReviewerWorkspaceError("WB_REVIEW_TASK_CONTEXT_INVALID") if ( context["review_target_kind"] != "task" @@ -315,7 +316,7 @@ def _public_packet(packet: dict[str, object]) -> dict[str, object]: artifacts = packet.get("artifacts") if not isinstance(artifacts, list): raise ReviewerWorkspaceError("WB_REVIEW_PACKET_INVALID") - return { + result = { **{key: value for key, value in packet.items() if key != "policy_roots"}, "artifacts": [ {key: value for key, value in item.items() if key != "content_base64"} @@ -323,6 +324,12 @@ def _public_packet(packet: dict[str, object]) -> dict[str, object]: if isinstance(item, dict) ], } + context = result.get("task_review_context") + if isinstance(context, dict) and "previous_review" in context: + result["task_review_context"] = { + key: value for key, value in context.items() if key != "previous_review" + } + return result def _sb_quote(path: Path) -> str: @@ -520,6 +527,9 @@ def create_reviewer_workspace( effective_source, effective_control, effective_protected ), "status": "active", + **({"task_review_previous_review": packet["task_review_context"]["previous_review"]} + if isinstance(packet.get("task_review_context"), dict) + and "previous_review" in packet["task_review_context"] else {}), } state_path.parent.mkdir(parents=True, exist_ok=True) state_path.write_text(json.dumps(state, indent=2, sort_keys=True) + "\n", encoding="utf-8") @@ -694,6 +704,98 @@ def run_sandboxed_validator(workspace: Path, argv: list[str]) -> subprocess.Comp return _run_sandboxed_process(workspace, argv) +def _task_product_judgment_review( + judgment: object, + *, + review_id: str, + context: dict[str, object], + packet: dict[str, object], + started_at: str, + completed_at: str, + previous_review: object = None, + integrated_stage: bool = False, +) -> dict[str, object]: + """Compose controller-owned review authority around a compact product judgment.""" + if not isinstance(judgment, dict) or set(judgment) != {"task_review"}: + raise ReviewerWorkspaceError("WB_REVIEW_TASK_OUTPUT_INVALID") + product = judgment["task_review"] + if not isinstance(product, dict) or set(product) != {"reviewed_head", "verdict", "findings"}: + raise ReviewerWorkspaceError("WB_REVIEW_TASK_OUTPUT_INVALID") + identity = context["target_identity"] + if ( + not isinstance(identity, dict) + or product["reviewed_head"] != ( + identity.get("source_tree") if integrated_stage else identity.get("revision") + ) + or product["verdict"] not in {"accept", "repair"} + or not isinstance(product["findings"], list) + ): + raise ReviewerWorkspaceError("WB_REVIEW_TASK_OUTPUT_INVALID") + findings = [] + for item in product["findings"]: + expected = {"finding_id", "severity", "requirement_id", "boundary", "evidence", "expected", "observed", "owner"} + if ( + not isinstance(item, dict) or set(item) != expected + or item["severity"] not in {"blocking", "advisory"} + or item["owner"] != "task_owner" + or not all(isinstance(item[key], str) and item[key].strip() for key in expected - {"severity"}) + ): + raise ReviewerWorkspaceError("WB_REVIEW_TASK_OUTPUT_INVALID") + advisory = item["severity"] == "advisory" + findings.append({ + "finding_id": item["finding_id"], "stage": "implementation", + "class": "advisory_enhancement" if advisory else "implementation_defect", + "severity": item["severity"], "first_broken_artifact": "implementation", + "obligation_basis": "none" if advisory else "accepted_requirement", + "evidence": [{ + "kind": "source", "locator": item["boundary"], + "digest_or_identity": _canonical_digest({ + "requirement_id": item["requirement_id"], "evidence": item["evidence"] + }), + "observation": f"{item['evidence']} Expected: {item['expected']} Observed: {item['observed']}", + }], + "target_identity": identity, + "summary": f"{item['requirement_id']}: {item['observed']}", + "recommended_owner": "backlog_owner" if advisory else "task_owner", + "disposition": "record_advisory" if advisory else "repair_task", + }) + artifacts = [ + {"path": item["locator"], "sha256": item["sha256"]} + for item in packet.get("artifacts", []) if isinstance(item, dict) + ] + result = { + "required": True, "reviewer_independent": True, "review_id": review_id, + "reviewed_head": identity["revision"], "review_mode": context.get("review_mode", "initial"), + "review_target_kind": "task", "repair_frontier": context.get("repair_frontier"), + "review_reset": context.get("review_reset"), "target_identity": identity, + "reviewer": { + "agent_id": context["agent_id"], "capability": context["capability"], + "authorship": "none", "repair_participation": "none", + "decision_participation": "none", "deliberation_participation": "none", + "context_origin": context["evidence_mode"], + }, + "evidence": { + "mode": context["evidence_mode"], "capabilities": ["product review judgment"], + "unavailable_evidence": [], "commands": [], "artifacts": artifacts, + }, + "verdict": product["verdict"], "findings": findings, + "started_at": started_at, "completed_at": completed_at, + "staleness": {"is_stale": False, "reason": None, "supersedes": None}, + } + if context.get("review_mode", "initial") == "repair" or context.get("review_reset") is not None: + if not isinstance(previous_review, dict): + raise ReviewerWorkspaceError("WB_REVIEW_TASK_CONTROL_INPUT_INVALID") + result["previous_review"] = previous_review + if integrated_stage: + result.pop("required") + result.pop("reviewer_independent") + result.pop("reviewed_head") + result["stage"] = "integrated_implementation" + result["review_target_kind"] = "stage" + result["verdict"] = "accepted" if product["verdict"] == "accept" else "repair" + return result + + def run_sandboxed_reviewer(workspace: Path, argv: list[str]) -> dict[str, object]: """Launch the entire reviewer under the frozen deny-default profile.""" workspace = workspace.expanduser().resolve() @@ -701,6 +803,15 @@ def run_sandboxed_reviewer(workspace: Path, argv: list[str]) -> dict[str, object packet, _ = _load_workspace(workspace) if _canonical_digest(packet) != state.get("packet_sha256") or _artifact_digest(workspace, packet) != state.get("evidence_digest"): raise ReviewerWorkspaceError("WB_REVIEW_EVIDENCE_MUTATED") + if "stage_review_context" in packet: + try: + _review_runtime().validate_stage_evidence( + workspace / "evidence/control", + _validate_stage_context(packet["stage_review_context"]), + packet, + ) + except (ValueError, OSError, SystemExit) as error: + raise ReviewerWorkspaceError("WB_REVIEW_STAGE_EVIDENCE_INCOMPLETE") from error started_at = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z") completed = _run_sandboxed_process(workspace, argv) if _artifact_digest(workspace, packet) != state.get("evidence_digest"): @@ -741,14 +852,30 @@ def run_sandboxed_reviewer(workspace: Path, argv: list[str]) -> dict[str, object else _validate_task_context(packet[context_key]) ) try: - review = json.loads(completed.stdout) + worker_output = json.loads(completed.stdout) + compact_integrated = ( + context_key == "stage_review_context" + and context.get("stage") == "integrated_implementation" + and isinstance(worker_output, dict) and "task_review" in worker_output + ) + review = ( + _task_product_judgment_review( + worker_output, review_id=review_id, context=context, packet=packet, + started_at=started_at, completed_at=receipt["completed_at"], + previous_review=state.get("task_review_previous_review"), + integrated_stage=compact_integrated, + ) + if context_key == "task_review_context" or compact_integrated + else worker_output + ) validated = ( _review_runtime().validate_stage_review(review) if context_key == "stage_review_context" else _review_runtime().validate_task_acceptance_review(review) ) except (ValueError, TypeError) as error: - raise ReviewerWorkspaceError("WB_REVIEW_STAGE_OUTPUT_INVALID") from error + code = "WB_REVIEW_TASK_OUTPUT_INVALID" if context_key == "task_review_context" else "WB_REVIEW_STAGE_OUTPUT_INVALID" + raise ReviewerWorkspaceError(code) from error mode = "direct_source" if validated.evidence["mode"] == "direct" else validated.evidence["mode"] review_context = { "review_mode": review.get("review_mode", "initial"), @@ -777,6 +904,8 @@ def run_sandboxed_reviewer(workspace: Path, argv: list[str]) -> dict[str, object raise ReviewerWorkspaceError("WB_REVIEW_STAGE_EVIDENCE_INCOMPLETE") from error receipt[context_key] = context receipt["review_result_sha256"] = _canonical_digest(review) + if context_key == "task_review_context" or compact_integrated: + receipt["review_result"] = review receipt["isolation"] = {"mechanism": "sandbox-exec", "network": "denied", "write_scope": "scratch"} receipt_path = (runtime_root / "receipts" / "reviewer-process" / f"{run_id}.json").resolve(strict=False) if not _inside(runtime_root, receipt_path): diff --git a/skills/dev-code-review/SKILL.md b/skills/dev-code-review/SKILL.md index 0a4448c..a02025f 100644 --- a/skills/dev-code-review/SKILL.md +++ b/skills/dev-code-review/SKILL.md @@ -1,33 +1,39 @@ --- name: dev-code-review -description: Use for an independent review of a completed implementation task before acceptance, integration, or downstream reliance on its result. +description: Use for an independent product review of a completed implementation task before acceptance, integration, or downstream reliance on its result. --- -# Code Review +# Product Code Review -Review against the accepted task and the exact tree or commit identity. Independence means the reviewer did not author the change; if that is false, disclose it and do not present the review as independent. +Judge one frozen candidate against only accepted product requirements and boundaries, the exact product source/diff identity, normalized validation observations, and unresolved product concerns. -Check: +Controller or orchestration runtime code is product source when the accepted task allocates it. Review that code normally. The exclusion below concerns bookkeeping supplied as review input, not words or APIs that occur in product code. -1. **grounded intent** — purpose, as-is source evidence, accepted decision authority, expected delta, and conflict status agree. -2. **task fit** — the change implements the grounded scope and avoids unrelated behavior. -3. **rules and methodology** — applicable repository rules and required development methods were followed. -4. **correctness and edge cases** — normal, boundary, failure, compatibility, and lifecycle paths behave correctly. -5. **test oracle and validation evidence** — the oracle follows grounded intent, RED failed for the intended reason when TDD applied, and fresh checks support the claim. -6. **knowledge disposition** — `none`, `update`, `supersede`, or `reclassify` is supported by task-local evidence and does not instruct persistence. -7. **unnecessary complexity** — no speculative abstraction, redundant path, or avoidable change radius was introduced. +## Excluded controller inputs -Return exactly this compact shape; omit no keys: +Do not receive or judge handoff or provenance records, receipt or publication bookkeeping, the review store, task or plan status/archive/history, knowledge persistence or knowledge disposition, reviewer history or identity rotation, or controller/evaluator mechanics. Repository rules are review constraints only when accepted product requirements incorporate them. + +Do not rerun validation. Do not confirm another review. The controller owns input integrity, execution isolation, reviewer independence, evidence publication, and lifecycle transitions. If required input is missing, inconsistent, or inaccessible, return an input/runner failure outside the product verdict; do not turn an infrastructure or control-input defect into a product finding or `blocked` verdict. + +Check task fit, correctness and edge cases (including failure, compatibility, and lifecycle behavior), support from normalized observations, and unnecessary complexity or unrelated change radius. + +Perform one review per one frozen candidate. After a product repair, perform one scoped rereview of the affected frontier. The same independent reviewer may be reused; independence is about participation and provenance, not identity rotation. + +Return exactly this compact product judgment. The controller supplies the native review envelope: ```yaml task_review: - reviewer_independent: true | false - verdict: accept | repair | blocked - reviewed_head: + reviewed_head: + verdict: accept | repair findings: - - severity: blocking | advisory - scope: specification | correctness | quality | validation | rule - finding: + - finding_id: + severity: blocking | advisory + requirement_id: + boundary: + evidence: + expected: + observed: + owner: task_owner ``` -Use `findings: []` when there are no findings. Green tests do not override a contradiction in intent, decision authority, or test oracle. Choose `repair` for an actionable defect and `blocked` when authoritative scope, source identity, conflict resolution, or capable evidence is unavailable. +Use `findings: []` when there are no findings. A blocking finding requires an accepted requirement or boundary, exact evidence, expected and observed behavior, and the task owner. Green observations do not override a product contradiction. diff --git a/skills/orch-create-handoff/SKILL.md b/skills/orch-create-handoff/SKILL.md index 9525eff..32c4852 100644 --- a/skills/orch-create-handoff/SKILL.md +++ b/skills/orch-create-handoff/SKILL.md @@ -61,7 +61,7 @@ Always include identity, related artifacts, result state, and concise summary. I - Do not store handoffs under `.work-bundle/knowledge/`. - Do not create new active `handoff-orch-*` artifacts or offer orchestration handoff creation as an active workflow path. - Do not implement source changes, edit application/test files, run migrations, apply patches, or execute plan tasks while creating a handoff. -- If the user also asks for implementation, finish the handoff artifact first, then stop and require an explicit `execute-plan` request. +- Create the handoff only for the initial executor result. If execution is already authorized, resume the owning workflow after that handoff without demanding repeated permission. Already accepted tasks consume their compact accepted result and do not create another handoff merely to acquire review, publication, or finalization facts. - Do not include raw chat logs, private reasoning, or unrelated history. - Do not include durable-knowledge recommendations, orchestration review recommendations, executor advice fields, or strategy advice in executor-result handoffs. - Stop if source artifact paths or current state are unknown. diff --git a/skills/orch-execute-plan/SKILL.md b/skills/orch-execute-plan/SKILL.md index 511de8f..db9c36c 100644 --- a/skills/orch-execute-plan/SKILL.md +++ b/skills/orch-execute-plan/SKILL.md @@ -47,7 +47,7 @@ python3 scripts/orch.py validate-executor-result --task --handoff review-blocked -> resume owning execution step +missing initial executor handoff + -> review-blocked -> resume initial result owner +accepted-task source repair + -> existing task owner -> claim-relevant validation -> scoped rereview +publication/status/archive control failure + -> controller owner -> reuse compact accepted result and completed review knowledge work or return evidence incomplete -> knowledge-blocked -> resume approved ks-* delegate-return path metadata/index/repository finalization incomplete diff --git a/tests/test_dev_skill_contracts.py b/tests/test_dev_skill_contracts.py index 0138a2e..4661473 100644 --- a/tests/test_dev_skill_contracts.py +++ b/tests/test_dev_skill_contracts.py @@ -105,27 +105,55 @@ def test_tdd_contract_names_cycle_applicability_and_exemptions() -> None: assert token in text -def test_code_review_contract_is_independent_and_emits_exact_shape() -> None: +def test_code_review_contract_is_product_only_and_emits_actionable_findings() -> None: text = skill_text("dev-code-review") for token in [ - "task fit", - "rules and methodology", + "accepted product requirements", + "exact product source/diff identity", + "normalized validation observations", + "unresolved product concerns", "correctness and edge cases", "unnecessary complexity", - "validation evidence", - "reviewer_independent: true | false", - "verdict: accept | repair | blocked", - "reviewed_head: ", - "severity: blocking | advisory", - "scope: specification | correctness | quality | validation | rule", - "finding: ", - "grounded intent", - "decision authority", - "test oracle", - "knowledge disposition", + "verdict: accept | repair", + "reviewed_head: ", + "finding_id:", + "requirement_id:", + "boundary:", + "evidence:", + "expected:", + "observed:", + "owner: task_owner", + "input/runner failure", + "one frozen candidate", + "one scoped rereview", + "same independent reviewer", ]: assert token in text + exclusions = text.split("## Excluded controller inputs", 1)[1] + for token in [ + "handoff", "receipt", "publication", "review store", "status", "archive", + "knowledge disposition", "identity rotation", "controller", "evaluator", + ]: + assert token in exclusions + assert "rerun validation" in text + assert "confirm another review" in text + assert "controller supplies the native review envelope" in text.lower() + assert "reviewer_independent" not in text + assert "verdict: accept | repair | blocked" not in text + + +def test_code_review_pressure_scenarios_cover_control_defects_and_non_trigger() -> None: + evals = json.loads((REPO_ROOT / "references/evals/development/evals.json").read_text())["evals"] + by_id = {item["id"]: item for item in evals} + assert { + "dev-review-product-defect", + "dev-review-control-input-failure", + "dev-review-adversarial-non-trigger", + } <= set(by_id) + assert "task_owner" in by_id["dev-review-product-defect"]["expected_output"] + assert "outside the product verdict" in by_id["dev-review-control-input-failure"]["expected_output"] + assert "does not invoke dev-code-review" in by_id["dev-review-adversarial-non-trigger"]["expected_output"] def test_mechanical_task_plan_contract_escalates_and_uses_exact_sections() -> None: diff --git a/tests/test_orchestration_accepted_result.py b/tests/test_orchestration_accepted_result.py index 13f49dd..10eead0 100644 --- a/tests/test_orchestration_accepted_result.py +++ b/tests/test_orchestration_accepted_result.py @@ -90,6 +90,7 @@ def _binding(root: Path) -> dict[str, object]: "execution_id": "exec-001", "repository_id": "repo-001", "execution_path": str(root), + "control_root": str(root), "git_identity": {"branch_ref": "refs/heads/main"}, "baseline": {"head": OID_A, "tree": OID_B}, "ownership": { @@ -101,6 +102,37 @@ def _binding(root: Path) -> dict[str, object]: } +def _record_validation_observation( + root: Path, binding: dict[str, object], task: dict[str, object] +) -> str: + item = task["validation"][0] + assert isinstance(item, dict) + item["evidence_reuse"] = { + "mode": "deterministic", "max_age_seconds": 3600, + "environment_inputs": [], "include_head": False, + } + evidence = execution_context.capture_repository_evidence(root) + + def observe(receipt: dict[str, object]) -> dict[str, object]: + receipt.update({ + "exit_code": 0, + "stdout_digest": "1" * 64, + "stderr_digest": "2" * 64, + "started_at": "2026-09-08T01:00:00Z", + "completed_at": "2026-09-08T01:00:01Z", + }) + return { + "id": item.get("id"), "command": item.get("command"), + "invariant_ids": item.get("invariant_ids", []), "result": "passed", + } + + observed = execution_context._completion_provenance_module().observe_validation( + binding, task, item, evidence, observe, + lambda: execution_context.capture_repository_evidence(root), + ) + return str(observed["observation_id"]) + + def _handoff() -> dict[str, object]: return { "type": "executor-result", @@ -395,17 +427,18 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor review_reference = review_runtime.publish_review( tmp_path, repair_review, current_target_identity=repaired_identity ) + observation_id = _record_validation_observation(tmp_path, binding, task) repaired = execution_context.materialize_accepted_task_repair_review( tmp_path, task, review_reference, accepted_at="2026-09-08T01:05:00Z", + validation_evidence_ids=[observation_id], ) for field in ( "baseline_identity", "executor_result_digest", - "validation_evidence_ids", "owner_identity", "knowledge_disposition", ): @@ -413,6 +446,7 @@ def test_standalone_repair_review_rematerializes_compact_result_without_executor assert repaired["accepted_source"]["head"] == reviewed_head assert repaired["accepted_source"]["tree"] == reviewed_tree assert repaired["review_id"] == "review-repair-001" + assert repaired["validation_evidence_ids"] == [observation_id] assert repaired["authority_projection"]["required_review_digest"] == execution_context.semantic_digest( execution_context._accepted_review_projection(repair_review) ) @@ -555,6 +589,11 @@ def git_result(arguments, **_kwargs): "build_accepted_task_result", lambda *_args, **_kwargs: pytest.fail("executor result replayed"), ) + monkeypatch.setattr( + execution_context, + "_claim_bound_validation_observations", + lambda *_args: [{"observation_id": "obs-current"}], + ) accepted = execution_context.materialize_accepted_task_review( tmp_path, @@ -565,13 +604,15 @@ def git_result(arguments, **_kwargs): "affected_task": "task-001", "authorized_lifecycle_action": "rematerialize_accepted_result", }, + validation_evidence_ids=["obs-current"], ) for field in ( - "baseline_identity", "executor_result_digest", "validation_evidence_ids", + "baseline_identity", "executor_result_digest", "owner_identity", "knowledge_disposition", ): assert accepted[field] == prior[field] + assert accepted["validation_evidence_ids"] == ["obs-current"] assert accepted["accepted_source"]["head"] == OID_C assert accepted["accepted_source"]["tree"] == OID_D assert accepted["authority_projection"] == execution_context._accepted_authority_projection( diff --git a/tests/test_orchestration_execution_context.py b/tests/test_orchestration_execution_context.py index d7c9b58..5644fe7 100644 --- a/tests/test_orchestration_execution_context.py +++ b/tests/test_orchestration_execution_context.py @@ -40,10 +40,12 @@ def test_product_review_candidate_excludes_handoff_and_publication_bookkeeping() diff="diff --git a/src/a.py b/src/a.py\n", changed_files=["M\tsrc/a.py"], changed_symbols=["run"], validation_observations=[{"id": "VAL-006", "result": "passed"}], - knowledge_disposition={"status": "none", "reason": "task local"}, ) encoded = json.dumps(candidate, sort_keys=True) - assert all(term not in encoded for term in ("handoff", "acceptance_review", "reviewer_run")) + assert all(term not in encoded for term in ( + "handoff", "acceptance_review", "reviewer_run", "knowledge_disposition", + "methodology", "allocated_rules", "semantic_authority", "evidence_capability", + )) assert candidate["task_authority"]["task_id"] == "task-006" @@ -1497,21 +1499,113 @@ def test_build_review_package_contains_only_bounded_task_diff_and_evidence(tmp_p assert "password: " in package assert "result: passed" in package assert "Confirm retry timing with the caller." in package - assert "scoped-rule" in package - assert "dev-test-driven-development" in package assert "## Review rubric" in package - assert "## Accepted Truth Basis" in package - assert "## Evidence capability" in package - assert "INV-001" in package - assert "VAL-001" in package - assert "closure_result" in package - assert "pending" in package - assert "## Knowledge disposition" in package - assert "No stable authority changed." in package + assert "## Knowledge disposition" not in package assert "SHOULD-NOT-APPEAR" not in package assert ".work-bundle/knowledge/notes" not in package +def test_initial_completed_result_can_prepare_required_review_without_future_verdict(tmp_path: Path) -> None: + root, _, task = workspace(tmp_path) + task.write_text(task.read_text().replace( + "acceptance_review:\n required: false\n", + "acceptance_review:\n required: true\n", + )) + base = committed_review_base(root) + handoff = write_executor_handoff( + root, " action: none\n reason: No stable authority changed.\n affected_authority: []\n" + ) + handoff.write_text(handoff.read_text().replace( + "result: {state: completed}\n", + "result: {state: completed}\nacceptance_review: {required: true, verdict: pending}\n", + )) + _enable_passing_observation(root, task, handoff) + + package = build_review_package(args(root, task, handoff=str(handoff), base=base, head=base)) + assert package.is_file() + with pytest.raises(SystemExit, match="cannot complete without"): + _validate_observed(_read_handoff(handoff), _compiled_brief(root, task)) + + +def test_postacceptance_review_package_ignores_stale_handoff_and_requires_current_observation(tmp_path: Path) -> None: + root, _, task = workspace(tmp_path) + base = committed_review_base(root) + handoff = write_executor_handoff( + root, " action: none\n reason: No stable authority changed.\n affected_authority: []\n" + ) + _set_process_validation( + task, PASSING_PROCESS, + evidence_reuse={ + "mode": "deterministic", "max_age_seconds": 3600, + "environment_inputs": ["PYTHONHASHSEED"], "include_head": False, + }, + ) + brief = _compiled_brief(root, task) + assert brief["validation"][0]["evidence_reuse"]["max_age_seconds"] == 3600 + _bind_task_execution(root, brief) + handoff.write_text( + handoff.read_text().replace(TASK_VALIDATION_COMMAND, json.dumps(PASSING_PROCESS)) + ) + validated = _validate_observed(_read_handoff(handoff), brief) + execution_context.materialize_accepted_task_result(root, brief, _read_handoff(handoff), validated) + + source = root / WRITE_SCOPE_FILE + source.write_text(source.read_text() + "\n# reviewed repair\n") + git(root, "add", WRITE_SCOPE_FILE) + git(root, "commit", "-qm", "repair") + binding = execution_context.load_task_execution_binding(root, "plan-001", "task-004") + repository_evidence = execution_context.capture_repository_evidence(root) + observed = execution_context._completion_provenance_module().observe_validation( + binding, brief, brief["validation"][0], repository_evidence, + lambda receipt: execution_context._observe_validation_item( + brief["validation"][0], root, brief, receipt + ), + lambda: execution_context.capture_repository_evidence(root), + ) + observation_id = observed["observation_id"] + + controller_resume = root / ".work-bundle/runtime/controller-resume.json" + controller_resume.parent.mkdir(parents=True, exist_ok=True) + controller_resume.write_text('{"publication": "retry"}\n') + head = git(root, "rev-parse", "HEAD") + + unrelated_check = {**brief["validation"][0], "id": "VAL-UNRELATED", "command": "true"} + unrelated_observed = execution_context._completion_provenance_module().observe_validation( + binding, brief, unrelated_check, execution_context.capture_repository_evidence(root), + lambda receipt: execution_context._observe_validation_item( + unrelated_check, root, brief, receipt + ), + lambda: execution_context.capture_repository_evidence(root), + ) + handoff.write_text("this: [is: stale") + + with pytest.raises(SystemExit, match="claim-bound|does not bind current task claims"): + build_review_package(args( + root, task, handoff=str(handoff), base=base, head=head, + validation_observation_id=[unrelated_observed["observation_id"]], + )) + + package = build_review_package(args( + root, task, handoff=str(handoff), base=base, head=head, + validation_observation_id=[observation_id], + )).read_text() + assert "reviewed repair" in package + assert observation_id in package + assert PASSING_PROCESS in package + assert "invariant_ids" in package + + material_source = root / "unrelated.txt" + material_source.write_text("new material source\n") + git(root, "add", "unrelated.txt") + git(root, "commit", "-qm", "material source change") + with pytest.raises(SystemExit, match="claim-bound"): + build_review_package(args( + root, task, handoff=str(handoff), base=base, + head=git(root, "rev-parse", "HEAD"), + validation_observation_id=[observation_id], + )) + + def test_build_review_package_resolves_git_refs_in_bound_execution_repository( tmp_path: Path, ) -> None: @@ -1724,8 +1818,8 @@ def test_build_review_package_receives_same_resolved_auth_semantics(tmp_path: Pa assert COMPILED_AUTHORITY in brief assert COMPILED_AUTHORITY in package assert ACCEPTED_CONSTRAINT in package - assert "## Accepted Truth Basis" in package - assert ACCEPTED_AUTHORITY_PATH not in package.split("## Accepted Truth Basis", 1)[1].split("## Allowed scope", 1)[0] + assert "## Accepted Truth Basis" not in package + assert ACCEPTED_AUTHORITY_PATH not in package assert DECOY_KNOWLEDGE not in package @@ -1776,9 +1870,10 @@ def test_build_review_package_accepts_allocated_auth_in_knowledge_disposition( assert COMPILED_AUTHORITY in package assert ACCEPTED_CONSTRAINT in package - assert f"action: {action}" in package + assert f"action: {action}" not in package assert ACCEPTED_AUTHORITY in package - assert ACCEPTED_AUTHORITY_PATH not in package.split("## Knowledge disposition", 1)[1].split("## Allocated", 1)[0] + assert "## Knowledge disposition" not in package + assert ACCEPTED_AUTHORITY_PATH not in package def test_build_review_package_rejects_unallocated_auth_in_knowledge_disposition(tmp_path: Path) -> None: @@ -3251,7 +3346,7 @@ def _validate_observed(handoff: dict, brief: dict) -> dict: def _set_process_validation(task: Path, command: str, **fields: object) -> None: - extra = "".join(f", {key}: {value}" for key, value in fields.items()) + extra = "".join(f", {key}: {json.dumps(value)}" for key, value in fields.items()) _set_task_validation( task, "validation:\n" diff --git a/tests/test_orchestration_reviews.py b/tests/test_orchestration_reviews.py index 47f15fc..1988a41 100644 --- a/tests/test_orchestration_reviews.py +++ b/tests/test_orchestration_reviews.py @@ -521,7 +521,7 @@ def test_target_only_packet_cannot_declare_direct_source(tmp_path, stage): review_runtime.validate_stage_evidence(tmp_path, packet["stage_review_context"], packet) -@pytest.mark.parametrize("removed", [None, "target", "plan_member", "verified_specification", "source_tree", "validation_evidence"]) +@pytest.mark.parametrize("removed", [None, "target", "plan_member", "verified_specification", "source_tree", "accepted_task_result"]) def test_complete_snapshot_gate_rechecks_membership_after_receipt_rehash(tmp_path, removed): import hashlib import review_runtime @@ -531,6 +531,7 @@ def test_complete_snapshot_gate_rechecks_membership_after_receipt_rehash(tmp_pat handoff = tmp_path / ".work-bundle/orchestration/handoff/executor/active/result.yaml" handoff.parent.mkdir(parents=True) handoff.write_text("related: {plan: plan-test, task: task-test}\nvalidation: {commands: [{command: test -f source.txt, result: passed}]}\n") + _write_compact_accepted_result(tmp_path) for args in (["init", "-q"], ["config", "user.name", "Test"], ["config", "user.email", "test@example.com"]): subprocess.run(["git", "-C", str(tmp_path), *args], check=True) (tmp_path / ".gitignore").write_text(".work-bundle/\n") @@ -587,7 +588,84 @@ def test_integrated_snapshot_requires_evidence_for_each_declared_check(tmp_path) handoff.parent.mkdir(parents=True) handoff.write_text("related: {plan: plan-test, task: task-test}\nvalidation: {commands: [{command: unrelated-check, result: passed}]}\n") _, missing = review_runtime.stage_evidence_requirements(tmp_path, "integrated_implementation", plan) - assert "validation_evidence:task-test" in missing + assert "accepted_task_result_missing:task-test" in missing + + +def _write_compact_accepted_result(root: Path, *, review_id: str | None = None) -> Path: + binding = root / ".work-bundle/runtime/execution/plan-test/task-test/execution-binding.json" + binding.parent.mkdir(parents=True, exist_ok=True) + authority = { + "task_digest": "1" * 64, "binding_digest": "2" * 64, + "scope_digest": "3" * 64, "validation_obligations_digest": "4" * 64, + "required_review_digest": "5" * 64, "ownership_digest": "6" * 64, + } + baseline = {"head": "a" * 40, "tree": "b" * 40} + knowledge = {"disposition": "none", "reason": "No durable knowledge delta."} + accepted = { + "schema": "accepted-task-result-v1", "plan_id": "plan-test", "task_id": "task-test", + "binding_id": "binding:plan-test:task-test", "baseline_identity": baseline, + "accepted_source": {"head": "c" * 40, "tree": "d" * 40}, + "authority_projection": authority, "executor_result_digest": "7" * 64, + "validation_evidence_ids": ["observation-val-1"], "review_id": review_id, + "owner_identity": {"delegated": True, "owner_kind": "subagent", "agent_id": "/root/task", "run_id": "run-1", "mechanism": "host-native"}, + "knowledge_disposition": knowledge, "accepted_at": "2026-09-08T00:00:00Z", "invalidation": None, + } + accepted["accepted_source"]["state_digest"] = review_runtime.accepted_result_state_digest(accepted) + binding.write_text(json.dumps({ + "plan_id": "plan-test", "task_id": "task-test", + "ownership": {"binding_id": "binding:plan-test:task-test"}, + "accepted_result": accepted, + })) + return binding + + +def test_integrated_snapshot_uses_compact_acceptance_not_handoff_history(tmp_path): + _, plan, _ = _reviewed_plan_fixture(tmp_path, provenance=False) + task = plan.parent / "task.md" + task.write_text("---\nid: task-test\nplan_id: plan-test\nvalidation: [{id: VAL-1, command: check-claim}]\n---\nTask\n") + binding = _write_compact_accepted_result(tmp_path) + misleading = tmp_path / ".work-bundle/orchestration/handoff/executor/active/broken.yaml" + misleading.parent.mkdir(parents=True) + misleading.write_text("invalid:\n badly indented\n historical: true\n") + + required, missing = review_runtime.stage_evidence_requirements( + tmp_path, "integrated_implementation", plan + ) + + assert missing == [] + assert required["control:" + binding.relative_to(tmp_path).as_posix()] == "accepted_task_result" + assert not any("handoff" in locator for locator in required) + + +def test_integrated_snapshot_includes_native_review_when_present_and_rejects_invalid_compact_authority(tmp_path, monkeypatch): + _, plan, _ = _reviewed_plan_fixture(tmp_path, provenance=False) + task = plan.parent / "task.md" + task.write_text("---\nid: task-test\nplan_id: plan-test\nvalidation: [{id: VAL-1, command: check-claim}]\n---\nTask\n") + binding = _write_compact_accepted_result(tmp_path, review_id="review-task-current") + accepted = json.loads(binding.read_text())["accepted_result"] + review = { + **stage_review("plan"), "required": True, "reviewer_independent": True, + "review_id": "review-task-current", "reviewed_head": accepted["accepted_source"]["head"], + "review_mode": "initial", "review_target_kind": "task", "repair_frontier": None, + "review_reset": None, "target_identity": { + "artifact_id": "task-test", "revision": accepted["accepted_source"]["head"], + "sha256": "8" * 64, "source_tree": accepted["accepted_source"]["tree"], + }, "verdict": "accept", + } + review_path = tmp_path / ".work-bundle/orchestration/reviews/review-task-current.json" + review_path.parent.mkdir(parents=True, exist_ok=True) + review_path.write_text(json.dumps(review)) + review_path.chmod(0o444) + monkeypatch.setattr(review_runtime, "_validate_reviewer_run", lambda *_: None) + required, missing = review_runtime.stage_evidence_requirements(tmp_path, "integrated_implementation", plan) + assert missing == [] + assert required["control:" + review_path.relative_to(tmp_path).as_posix()] == "accepted_task_review" + + payload = json.loads(binding.read_text()) + payload["accepted_result"]["accepted_source"]["state_digest"] = "0" * 64 + binding.write_text(json.dumps(payload)) + _, missing = review_runtime.stage_evidence_requirements(tmp_path, "integrated_implementation", plan) + assert "accepted_task_result_invalid:task-test" in missing def test_manually_authored_accepted_review_cannot_advance_lifecycle(tmp_path): diff --git a/tests/test_reviewer_workspace.py b/tests/test_reviewer_workspace.py index 562284f..4ef03be 100644 --- a/tests/test_reviewer_workspace.py +++ b/tests/test_reviewer_workspace.py @@ -149,41 +149,135 @@ def test_task_review_worker_output_receives_native_bound_receipt( task_review_context=context, ) created = create_reviewer_workspace(runtime, "review-task-native", direct) - review = { - "required": True, - "reviewer_independent": True, - "review_id": "review-task-native", + judgment = {"task_review": { "reviewed_head": head, - "review_mode": "initial", - "review_target_kind": "task", - "repair_frontier": None, - "review_reset": None, - "target_identity": identity, - "reviewer": { - "agent_id": "reviewer-task", "capability": "judgment", - "authorship": "none", "repair_participation": "none", - "decision_participation": "none", "deliberation_participation": "none", - "context_origin": "reproducible_snapshot", - }, - "evidence": { - "mode": "reproducible_snapshot", "capabilities": ["frozen source"], - "unavailable_evidence": [], "commands": [], - "artifacts": [{"path": direct["artifacts"][0]["locator"], "sha256": direct["artifacts"][0]["sha256"]}], - }, "verdict": "accept", "findings": [], - "started_at": "2026-09-08T00:00:00Z", "completed_at": "2026-09-08T00:01:00Z", - "staleness": {"is_stale": False, "reason": None, "supersedes": None}, - } + }} with patch.object( reviewer_workspace, "_run_sandboxed_process", - return_value=subprocess.CompletedProcess(["reviewer"], 0, json.dumps(review), ""), + return_value=subprocess.CompletedProcess(["reviewer"], 0, json.dumps(judgment), ""), ): receipt = reviewer_workspace.run_sandboxed_reviewer(Path(str(created["workspace_path"])), ["reviewer"]) assert receipt["status"] == "passed" assert receipt["task_review_context"]["target_identity"] == identity assert set(receipt["reviewer_run"]) == {"run_id", "sha256"} + assert receipt["review_result"]["reviewer"]["agent_id"] == "reviewer-task" + assert receipt["review_result"]["verdict"] == "accept" + + +def test_compact_task_judgment_composes_repair_and_reset_predecessors() -> None: + runtime = reviewer_workspace + old_identity = {"artifact_id": "task-006", "revision": "a" * 40, "sha256": "1" * 64, "source_tree": "b" * 40} + base_context = { + "target_identity": old_identity, "agent_id": "reviewer-task", "capability": "judgment", + "execution_id": "review-execution-task", "evidence_mode": "direct_source", + "review_mode": "initial", "review_target_kind": "task", "repair_frontier": None, + "review_reset": None, + } + blocking = {"task_review": {"reviewed_head": old_identity["revision"], "verdict": "repair", "findings": [{ + "finding_id": "finding-product", "severity": "blocking", "requirement_id": "REQ-1", + "boundary": "src/product.py:run", "evidence": "return value differs", "expected": "one", + "observed": "zero", "owner": "task_owner", + }]}} + previous = runtime._task_product_judgment_review( + blocking, review_id="review-prior", context=base_context, packet={"artifacts": []}, + started_at="2026-09-08T00:00:00Z", completed_at="2026-09-08T00:01:00Z", + ) + new_identity = {"artifact_id": "task-006", "revision": "c" * 40, "sha256": "2" * 64, "source_tree": "d" * 40} + repair_context = {**base_context, "target_identity": new_identity, "review_mode": "repair", "repair_frontier": { + "prior_review_id": "review-prior", "blocking_finding_ids": ["finding-product"], + "previous_reviewed_identity": old_identity, "repaired_identity": new_identity, + "affected_boundaries": ["src/product.py:run"], + "frozen_evidence_reference": reviewer_workspace._review_runtime().review_evidence_identity(previous), + }} + repaired = runtime._task_product_judgment_review( + {"task_review": {"reviewed_head": new_identity["revision"], "verdict": "accept", "findings": []}}, + review_id="review-repaired", context=repair_context, packet={"artifacts": []}, + started_at="2026-09-08T00:02:00Z", completed_at="2026-09-08T00:03:00Z", + previous_review=previous, + ) + assert reviewer_workspace._review_runtime().validate_task_acceptance_review(repaired).verdict == "accepted" + + reset_context = { + **base_context, "target_identity": new_identity, + "review_reset": {"prior_review_id": "review-prior", "reason_class": "scope", "reason": "Accepted scope changed."}, + } + reset = runtime._task_product_judgment_review( + {"task_review": {"reviewed_head": new_identity["revision"], "verdict": "accept", "findings": []}}, + review_id="review-reset", context=reset_context, packet={"artifacts": []}, + started_at="2026-09-08T00:02:00Z", completed_at="2026-09-08T00:03:00Z", + previous_review=previous, + ) + assert reviewer_workspace._review_runtime().validate_task_acceptance_review(reset).verdict == "accepted" + + +def test_compact_integrated_product_judgment_gets_controller_owned_stage_envelope() -> None: + identity = {"artifact_id": "plan-006", "revision": "6", "sha256": "1" * 64, "source_tree": "b" * 40} + context = { + "stage": "integrated_implementation", "target_identity": identity, + "target_locator": "control:.work-bundle/orchestration/plan/active/plan.md", + "agent_id": "reviewer-integrated", "capability": "judgment", + "execution_id": "review-execution-integrated", "evidence_mode": "direct_source", + } + review = reviewer_workspace._task_product_judgment_review( + {"task_review": {"reviewed_head": identity["source_tree"], "verdict": "accept", "findings": []}}, + review_id="review-integrated", context=context, packet={"artifacts": []}, + started_at="2026-09-08T00:00:00Z", completed_at="2026-09-08T00:01:00Z", + integrated_stage=True, + ) + validated = reviewer_workspace._review_runtime().validate_stage_review(review) + assert validated.stage == "integrated_implementation" + assert validated.verdict == "accepted" + + +def test_incomplete_stage_snapshot_fails_before_reviewer_process_launch( + review_roots: tuple[Path, Path, Path] +) -> None: + source, control, runtime = review_roots + subprocess.run(["git", "init", "-q", str(source)], check=True) + subprocess.run(["git", "-C", str(source), "add", "src/target.py", ".wor105-review-sentinel"], check=True) + subprocess.run( + ["git", "-C", str(source), "-c", "user.name=Test", "-c", "user.email=test@example.invalid", "commit", "-qm", "fixture"], + check=True, + ) + plan = control / ".work-bundle/orchestration/plan/active/plan.md" + plan.parent.mkdir(parents=True) + spec = control / ".work-bundle/orchestration/spec/active/spec.md" + spec.parent.mkdir(parents=True) + spec.write_text("---\nid: spec-preflight\nstatus: verified\n---\nSpec\n", encoding="utf-8") + plan.write_text("---\nid: plan-preflight\nstatus: Planned\nsource_spec: [spec-preflight]\n---\nPlan\n", encoding="utf-8") + task = plan.parent / "task.md" + task.write_text( + "---\nid: task-preflight\nplan_id: plan-preflight\nvalidation: [{id: VAL-1, command: true}]\n---\nTask\n", + encoding="utf-8", + ) + locator = "control:" + plan.relative_to(control).as_posix() + context = { + "stage": "integrated_implementation", + "target_identity": reviewer_workspace._review_runtime().stage_target_identity( + control, "integrated_implementation", plan, source_root=source + ), + "target_locator": locator, + "agent_id": "reviewer-preflight", + "capability": "judgment", + "execution_id": "reviewer-preflight-run", + "evidence_mode": "direct_source", + } + incomplete = build_direct_evidence_packet( + source_root=source, control_root=control, + protected_roots=[control / "credentials"], artifacts=[locator], search_roots=[], + validators=[], sentinels=[], network_state="denied", stage_review_context=context, + ) + assert incomplete["stage_evidence_manifest"]["missing"] + created = create_reviewer_workspace(runtime, "review-preflight", incomplete) + with patch.object(reviewer_workspace, "_run_sandboxed_process") as launch: + with pytest.raises(ReviewerWorkspaceError, match="STAGE_EVIDENCE_INCOMPLETE"): + reviewer_workspace.run_sandboxed_reviewer( + Path(str(created["workspace_path"])), ["reviewer"] + ) + launch.assert_not_called() def test_bounded_read_search_and_validators_are_allowed(review_roots: tuple[Path, Path, Path]) -> None: From 962da878550a924407c58f00a4143ccfea3ecf51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E5=8D=87=E9=BE=99?= Date: Wed, 9 Sep 2026 00:38:48 +0800 Subject: [PATCH 48/48] fix(orchestration): bind integrated acceptance to current task authority Reuse canonical current-authority checks during read-only integrated preparation. Preserve product repair findings without replaying control history and align contract assertions with first-owner routing. Refs WOR-112. --- scripts/orchestration/execution_context.py | 39 ++++-- scripts/orchestration/review_runtime.py | 20 ++- .../test_orchestration_context_projection.py | 6 +- tests/test_orchestration_reviews.py | 121 ++++++++++++++---- .../test_orchestration_skill_rule_boundary.py | 8 +- .../test_orchestration_workflow_contracts.py | 10 +- tests/test_rule_contracts.py | 3 +- 7 files changed, 160 insertions(+), 47 deletions(-) diff --git a/scripts/orchestration/execution_context.py b/scripts/orchestration/execution_context.py index 69b12a9..f49c524 100644 --- a/scripts/orchestration/execution_context.py +++ b/scripts/orchestration/execution_context.py @@ -4526,6 +4526,20 @@ def _assert_no_source_local_execution_artifacts( ) +def compile_task_authority(root: Path, task_path: Path) -> dict[str, Any]: + """Compile current task authority without materializing runtime artifacts.""" + + compile_args = argparse.Namespace( + project_root=str(root), + workspace_root=str(root), + task=str(task_path), + handoff=None, + base=None, + head=None, + ) + return _compile_task_brief(compile_args)[1]["task_brief"] + + def static_task_brief(root: Path, task_path: Path) -> dict[str, Any]: """Compile one task's static authority without runtime bindings or dependency results.""" @@ -4556,15 +4570,7 @@ def static_task_brief(root: Path, task_path: Path) -> dict[str, Any]: cleanup_baselines=cleanup_baselines, planning_sources=planning_sources, ) - compile_args = argparse.Namespace( - project_root=str(root), - workspace_root=str(root), - task=str(task_path), - handoff=None, - base=None, - head=None, - ) - return _compile_task_brief(compile_args)[1]["task_brief"] + return compile_task_authority(root, task_path) def static_plan_task_admission( @@ -4934,6 +4940,7 @@ def build_product_review_candidate( changed_files: Sequence[str], changed_symbols: Sequence[str], validation_observations: Sequence[Mapping[str, Any]], + repair_context: Mapping[str, Any] | None = None, knowledge_disposition: Mapping[str, Any] | None = None, unresolved: Sequence[Any] = (), ) -> dict[str, Any]: @@ -4960,6 +4967,14 @@ def build_product_review_candidate( "changed_files": list(changed_files), }, "validation_observations": [dict(item) for item in validation_observations], + "repair_context": ( + { + "blocking_finding_ids": list(_as_list(repair_context.get("blocking_finding_ids"))), + "affected_boundaries": list(_as_list(repair_context.get("affected_boundaries"))), + } + if isinstance(repair_context, Mapping) + else None + ), "unresolved": list(unresolved), } forbidden = {"handoff", "acceptance_review", "publication", "reviewer_run"} @@ -5126,6 +5141,7 @@ def build_review_package(args: argparse.Namespace) -> Path: changed_files=name_status, changed_symbols=symbols, validation_observations=evidence_projection, + repair_context=repair_frontier, unresolved=unresolved, ) authority = candidate["task_authority"] @@ -5160,6 +5176,11 @@ def build_review_package(args: argparse.Namespace) -> Path: "## Validation reported", *_markdown_items(candidate["validation_observations"]), "", + "## Product repair context", + *_markdown_items( + [candidate["repair_context"]] if candidate["repair_context"] is not None else [] + ), + "", "## Unresolved product concerns", *_markdown_items(candidate["unresolved"]), "", diff --git a/scripts/orchestration/review_runtime.py b/scripts/orchestration/review_runtime.py index e4dcd4c..38a82cb 100644 --- a/scripts/orchestration/review_runtime.py +++ b/scripts/orchestration/review_runtime.py @@ -190,12 +190,19 @@ def accepted_result_state_digest(accepted: Mapping[str, Any]) -> str: def _accepted_task_stage_evidence( - root: Path, plan_id: str, task_id: str, *, validate_native_receipt: bool + root: Path, + plan_id: str, + task_id: str, + task_path: Path, + *, + validate_native_receipt: bool, ) -> tuple[Path | None, Path | None, str | None]: binding = root / ".work-bundle/runtime/execution" / plan_id / task_id / "execution-binding.json" if binding.is_symlink() or not binding.is_file() or not binding.resolve().is_relative_to(root.resolve()): return None, None, f"accepted_task_result_missing:{task_id}" try: + from execution_context import assert_accepted_task_result_current, compile_task_authority + payload = _mapping(json.loads(binding.read_text()), "task execution binding") accepted = _mapping(payload.get("accepted_result"), "accepted task result") fields = set(accepted) @@ -222,6 +229,15 @@ def _accepted_task_stage_evidence( or source.get("state_digest") != accepted_result_state_digest(accepted) ): raise ReviewContractError("accepted task result binding or evidence is invalid") + if validate_native_receipt: + try: + current_task = compile_task_authority(root, task_path) + except SystemExit as error: + raise ReviewContractError(str(error)) from error + try: + assert_accepted_task_result_current(current_task, payload, accepted) + except SystemExit as error: + raise ReviewContractError(str(error)) from error review_path = None if accepted.get("review_id"): review_path = _review_store_path(root, str(accepted["review_id"])) @@ -314,7 +330,7 @@ def control(path: Path, role: str) -> None: continue task_id = str(item.get("id") or "") binding, review, failure = _accepted_task_stage_evidence( - root, str(data.get("id") or ""), task_id, + root, str(data.get("id") or ""), task_id, member, validate_native_receipt=validate_native_receipts, ) if failure: diff --git a/tests/test_orchestration_context_projection.py b/tests/test_orchestration_context_projection.py index 60a77d7..659dfb7 100644 --- a/tests/test_orchestration_context_projection.py +++ b/tests/test_orchestration_context_projection.py @@ -142,7 +142,8 @@ def test_ctx_01_repair_package_uses_frontier_without_reacquiring_review_history( assert "Review mode: repair" in package assert "RF-FINDING-1" in package - assert execution_context.semantic_digest("frozen") in package + assert "compile_task" in package + assert execution_context.semantic_digest("frozen") not in package assert "MUST-NOT-BE-PROJECTED" not in package assert f"Base: {base}" in package and f"Head: {head}" in package @@ -194,6 +195,9 @@ def test_ctx_04_success_evidence_projects_compact_receipt_not_history_or_stdout( assert projected == [{ "id": "VAL-001", + "command": "pytest -q", + "invariant_ids": [], + "observation_id": "observation-001", "digest": execution_context.semantic_digest({"command": "pytest -q", "result": "passed"}), "result": "passed", "boundary": "component", diff --git a/tests/test_orchestration_reviews.py b/tests/test_orchestration_reviews.py index 1988a41..68113e3 100644 --- a/tests/test_orchestration_reviews.py +++ b/tests/test_orchestration_reviews.py @@ -473,12 +473,21 @@ def test_lifecycle_gate_reads_current_artifact_not_claimed_staleness(tmp_path): def _reviewed_plan_fixture(root, *, provenance=True): import review_runtime orch = root / ".work-bundle/orchestration" + metadata = root / ".work-bundle/project.yaml" + metadata.parent.mkdir(parents=True, exist_ok=True) + metadata.write_text( + f"metadata_version: 3\nworkspace_root: {root}\nworkspace_mode: single-repository\n" + ) spec = orch / "spec/active/spec.md" plan = orch / "plan/active/plan.md" - for path, text in ((spec, "id: spec-test\nstatus: verified"), + for path, text in ((spec, "id: spec-test\nstatus: verified\nrequirements: [{id: REQ-001, requirement: Preserve accepted stage authority.}]"), (plan, "id: plan-test\nstatus: Planned\nsource_spec: [spec-test]")): path.parent.mkdir(parents=True, exist_ok=True) - path.write_text(f"---\n{text}\n---\nOriginal body\n") + body = ( + "- **REQ-001**: Preserve accepted stage authority.\nOriginal body\n" + if path == spec else "Original body\n" + ) + path.write_text(f"---\n{text}\n---\n{body}") reviews = orch / "reviews" reviews.mkdir() for stage, identity in (("specification", review_runtime.artifact_review_identity(spec)), @@ -491,6 +500,26 @@ def _reviewed_plan_fixture(root, *, provenance=True): return spec, plan, reviews +def _write_stage_task(plan: Path, *, review_required: bool = False, command: str = "check-claim") -> Path: + task = plan.parent / "task.md" + task.write_text( + "---\n" + "id: task-test\nplan_id: plan-test\nphase_id: phase-test\ndepends_on: []\n" + "goal: Preserve accepted stage authority.\n" + "source_ids: [REQ-001]\n" + "truth_basis: {purpose: Preserve authority, as_is_evidence: [source.txt], decision_authority: [none-relevant], expected_delta: [stage authority], conflict_status: clear}\n" + "files: {read: [source.txt], write: [], forbidden: [credentials/**]}\n" + "methodology: {primary: tdd, skills: [dev-test-driven-development]}\n" + "allocated_rules: []\n" + "executor_profile: {capability: standard, context_mode: compiled-brief}\n" + f"acceptance_review: {{required: {str(review_required).lower()}}}\n" + "evidence_capability: {result: mapped, reason: Direct command proves the stage claim, invariants: [{id: INV-STAGE, source_ids: [REQ-001], invariant: Accepted authority remains current, boundary: component, oracle: VAL-1, capability_reason: Direct command can falsify drift, freshness: current_task_batch, task_id: task-test, evidence_ids: [VAL-1], closure_result: pending}]}\n" + f"validation: [{{id: VAL-1, kind: process, command: {json.dumps(command)}, invariant_ids: [INV-STAGE], capability_reason: Direct command can falsify drift, proves: REQ-001, expected: passed}}]\n" + "---\nTask\n" + ) + return task + + @pytest.mark.parametrize("stage", ["plan", "integrated_implementation"]) def test_target_only_packet_cannot_declare_direct_source(tmp_path, stage): import reviewer_workspace @@ -526,8 +555,7 @@ def test_complete_snapshot_gate_rechecks_membership_after_receipt_rehash(tmp_pat import hashlib import review_runtime _, plan, _ = _reviewed_plan_fixture(tmp_path, provenance=False) - task = plan.parent / "task.md" - task.write_text("---\nid: task-test\nplan_id: plan-test\nvalidation: [{kind: process, command: test -f source.txt, expected: exit 0}]\n---\nTask\n") + task = _write_stage_task(plan, command="test -f source.txt") handoff = tmp_path / ".work-bundle/orchestration/handoff/executor/active/result.yaml" handoff.parent.mkdir(parents=True) handoff.write_text("related: {plan: plan-test, task: task-test}\nvalidation: {commands: [{command: test -f source.txt, result: passed}]}\n") @@ -582,8 +610,7 @@ def test_plan_snapshot_requires_verified_linked_specification(tmp_path): def test_integrated_snapshot_requires_evidence_for_each_declared_check(tmp_path): import review_runtime _, plan, _ = _reviewed_plan_fixture(tmp_path, provenance=False) - task = plan.parent / "task.md" - task.write_text("---\nid: task-test\nplan_id: plan-test\nvalidation: [{command: check-claim}]\n---\nTask\n") + task = _write_stage_task(plan) handoff = tmp_path / ".work-bundle/orchestration/handoff/executor/active/result.yaml" handoff.parent.mkdir(parents=True) handoff.write_text("related: {plan: plan-test, task: task-test}\nvalidation: {commands: [{command: unrelated-check, result: passed}]}\n") @@ -591,39 +618,55 @@ def test_integrated_snapshot_requires_evidence_for_each_declared_check(tmp_path) assert "accepted_task_result_missing:task-test" in missing -def _write_compact_accepted_result(root: Path, *, review_id: str | None = None) -> Path: +def _write_compact_accepted_result( + root: Path, *, task: Path | None = None, review_id: str | None = None +) -> Path: + import execution_context + + task = task or _write_stage_task(root / ".work-bundle/orchestration/plan/active/plan.md") + compiled_task = execution_context.static_task_brief(root, task) binding = root / ".work-bundle/runtime/execution/plan-test/task-test/execution-binding.json" binding.parent.mkdir(parents=True, exist_ok=True) - authority = { - "task_digest": "1" * 64, "binding_digest": "2" * 64, - "scope_digest": "3" * 64, "validation_obligations_digest": "4" * 64, - "required_review_digest": "5" * 64, "ownership_digest": "6" * 64, - } baseline = {"head": "a" * 40, "tree": "b" * 40} - knowledge = {"disposition": "none", "reason": "No durable knowledge delta."} + owner = {"delegated": True, "owner_kind": "subagent", "agent_id": "/root/task", "run_id": "run-1", "mechanism": "host-native"} + binding_payload = { + "plan_id": "plan-test", "task_id": "task-test", + "workspace_id": "workspace-test", "execution_id": "execution-test", + "repository_id": "repository-test", "execution_path": str(root.resolve()), + "git_identity": {}, "baseline": baseline, + "ownership": {"binding_id": "binding:plan-test:task-test", "original_owner": "task-test"}, + } + accepted_review = { + "required": review_id is not None, "review_id": review_id, + "verdict": "accept" if review_id is not None else None, + } + authority = execution_context._accepted_authority_projection( + compiled_task, binding_payload, accepted_review=accepted_review, owner_identity=owner + ) + knowledge = {"action": "none", "reason": "No durable knowledge delta.", "affected_authority": []} accepted = { "schema": "accepted-task-result-v1", "plan_id": "plan-test", "task_id": "task-test", "binding_id": "binding:plan-test:task-test", "baseline_identity": baseline, "accepted_source": {"head": "c" * 40, "tree": "d" * 40}, "authority_projection": authority, "executor_result_digest": "7" * 64, "validation_evidence_ids": ["observation-val-1"], "review_id": review_id, - "owner_identity": {"delegated": True, "owner_kind": "subagent", "agent_id": "/root/task", "run_id": "run-1", "mechanism": "host-native"}, + "owner_identity": owner, "knowledge_disposition": knowledge, "accepted_at": "2026-09-08T00:00:00Z", "invalidation": None, } accepted["accepted_source"]["state_digest"] = review_runtime.accepted_result_state_digest(accepted) - binding.write_text(json.dumps({ - "plan_id": "plan-test", "task_id": "task-test", - "ownership": {"binding_id": "binding:plan-test:task-test"}, - "accepted_result": accepted, - })) + binding_payload["accepted_result"] = accepted + binding.write_text(json.dumps(binding_payload)) + task_brief = binding.with_name("task-brief.yaml") + task_brief.write_text( + "\n".join(execution_context._dump_yaml({"task_brief": compiled_task})) + "\n" + ) return binding def test_integrated_snapshot_uses_compact_acceptance_not_handoff_history(tmp_path): _, plan, _ = _reviewed_plan_fixture(tmp_path, provenance=False) - task = plan.parent / "task.md" - task.write_text("---\nid: task-test\nplan_id: plan-test\nvalidation: [{id: VAL-1, command: check-claim}]\n---\nTask\n") - binding = _write_compact_accepted_result(tmp_path) + task = _write_stage_task(plan) + binding = _write_compact_accepted_result(tmp_path, task=task) misleading = tmp_path / ".work-bundle/orchestration/handoff/executor/active/broken.yaml" misleading.parent.mkdir(parents=True) misleading.write_text("invalid:\n badly indented\n historical: true\n") @@ -639,9 +682,8 @@ def test_integrated_snapshot_uses_compact_acceptance_not_handoff_history(tmp_pat def test_integrated_snapshot_includes_native_review_when_present_and_rejects_invalid_compact_authority(tmp_path, monkeypatch): _, plan, _ = _reviewed_plan_fixture(tmp_path, provenance=False) - task = plan.parent / "task.md" - task.write_text("---\nid: task-test\nplan_id: plan-test\nvalidation: [{id: VAL-1, command: check-claim}]\n---\nTask\n") - binding = _write_compact_accepted_result(tmp_path, review_id="review-task-current") + task = _write_stage_task(plan, review_required=True) + binding = _write_compact_accepted_result(tmp_path, task=task, review_id="review-task-current") accepted = json.loads(binding.read_text())["accepted_result"] review = { **stage_review("plan"), "required": True, "reviewer_independent": True, @@ -668,6 +710,35 @@ def test_integrated_snapshot_includes_native_review_when_present_and_rejects_inv assert "accepted_task_result_invalid:task-test" in missing +@pytest.mark.parametrize("mutation", ["task", "scope", "validation", "binding"]) +def test_integrated_snapshot_rejects_self_consistent_accepted_result_after_current_authority_drift( + tmp_path: Path, mutation: str +) -> None: + _, plan, _ = _reviewed_plan_fixture(tmp_path, provenance=False) + task = _write_stage_task(plan) + binding = _write_compact_accepted_result(tmp_path, task=task) + accepted_before = json.loads(binding.read_text())["accepted_result"] + assert accepted_before["accepted_source"]["state_digest"] == review_runtime.accepted_result_state_digest( + accepted_before + ) + + if mutation == "task": + task.write_text(task.read_text().replace("depends_on: []", "depends_on: [task-prior]")) + elif mutation == "scope": + task.write_text(task.read_text().replace("read: [source.txt]", "read: [source.txt, other.txt]")) + elif mutation == "validation": + task.write_text(task.read_text().replace("command: \"check-claim\"", "command: \"changed-check\"")) + else: + payload = json.loads(binding.read_text()) + payload["workspace_id"] = "workspace-changed" + binding.write_text(json.dumps(payload)) + + _, missing = review_runtime.stage_evidence_requirements( + tmp_path, "integrated_implementation", plan + ) + assert "accepted_task_result_invalid:task-test" in missing + + def test_manually_authored_accepted_review_cannot_advance_lifecycle(tmp_path): import review_runtime spec, _, _ = _reviewed_plan_fixture(tmp_path, provenance=False) diff --git a/tests/test_orchestration_skill_rule_boundary.py b/tests/test_orchestration_skill_rule_boundary.py index d0c9535..996ce18 100644 --- a/tests/test_orchestration_skill_rule_boundary.py +++ b/tests/test_orchestration_skill_rule_boundary.py @@ -127,7 +127,7 @@ def test_execute_skill_uses_compiler_independent_review_and_typed_blockers() -> "TaskOwnershipScheduler.validate_acceptance", "there is no controller or single-agent fallback", "must not implement or repair task write scope", - "After two failed repair rounds", + "one scoped rereview", "acceptance_review.required: true", "review_required: true", "does not require `verdict: accept`", @@ -288,7 +288,7 @@ def test_durable_owners_state_current_acceptance_and_review_semantics() -> None: ], "rules/orchestration/orch-review-completion.md": [ "reviewer infrastructure or provider failure", - "same immutable review package", + "publication-only/control resume", "finding-scoped repair review", "previous finding/evidence frontier", ], @@ -300,8 +300,8 @@ def test_durable_owners_state_current_acceptance_and_review_semantics() -> None: "skills/orch-execute-plan/SKILL.md": [ "compact accepted result", "acceptance once", - "same immutable review package", - "previous finding/evidence frontier", + "preserve the immutable package", + "affected frontier", ], "skills/orch-review-plan/SKILL.md": [ "compact accepted results", diff --git a/tests/test_orchestration_workflow_contracts.py b/tests/test_orchestration_workflow_contracts.py index f3b66f6..82e5baa 100644 --- a/tests/test_orchestration_workflow_contracts.py +++ b/tests/test_orchestration_workflow_contracts.py @@ -988,18 +988,18 @@ def test_workflow_separates_durable_artifacts_from_runtime_packets() -> None: def test_workflow_assigns_review_ownership_and_repair_loop() -> None: workflow = read("references/assets/orchestration/workflow.md") for token in [ - "Reviewers own acceptance judgment", + "Product reviewers judge accepted product requirements", "Schedulers own dependencies", "they do not perform code-quality review", "requires a subagent owner for every task", "fails closed before task mutation", "dispatch before any wait", - "After two failed low-cost repair rounds", + "one scoped rereview", "A task becomes `Completed` only when", "`Completed` does not require `verdict: accept` unless review was required", "optional task review when acceptance_review.required: true", "accepted Truth Basis", - "test oracle", + "normalized validation observations", ]: assert token in workflow @@ -1027,11 +1027,11 @@ def test_workflow_uses_accepted_results_without_lifecycle_replay() -> None: def test_review_rule_uses_typed_resume_routing() -> None: rule = read("rules/orchestration/orch-review-completion.md") for token in [ - "review-blocked", "knowledge-blocked", "repository-blocked", "workspace-blocked", - "resume the owning execution step", + "Route missing evidence to its first owner", + "publication-only/control resume", "plan repair only for a decomposition defect", "specification repair only for a requirement, design, or authority defect", "Do not create a repair specification for every failed review gate", diff --git a/tests/test_rule_contracts.py b/tests/test_rule_contracts.py index 9dd5845..ba4af0c 100644 --- a/tests/test_rule_contracts.py +++ b/tests/test_rule_contracts.py @@ -547,7 +547,8 @@ def test_orchestration_rules_require_contract_barrier_and_review_settlement_evid assert "universal task-review evidence" in review assert "implementation-review agent" in review assert "explicitly required" in review - assert "Route missing handoff, status, validation, or review evidence to `review-blocked`" in review + assert "Route missing evidence to its first owner" in review + assert "publication-only/control resume uses the compact accepted result" in review assert "Route incomplete durable knowledge work to `knowledge-blocked`" in review assert "Create or require plan repair only for a decomposition defect" in review assert "specification repair only for a requirement, design, or authority defect" in review