Compare commits

...
4 changed files with 143 additions and 15 deletions
@@ -112,6 +112,27 @@ class AuthorityGitExecutor:
raise AuthorityGitError("AUTHORITY_CAS_READBACK_MISMATCH")
return readback
def create_task_branch_CAS(self, branch: str, new_oid: str) -> str:
"""Create an agent branch only if the authoritative ref is absent."""
if not TASK_BRANCH_RE.fullmatch(branch):
raise AuthorityGitError("TASK_BRANCH_SCOPE_DENIED")
_oid(new_oid, "new_oid")
current = self.read_ref(branch)
if current is not None:
if current == new_oid:
return new_oid
raise AuthorityGitError("AUTHORITY_BRANCH_ALREADY_EXISTS")
try:
self._git(["update-ref", branch, new_oid, "0" * 40])
except AuthorityGitError as exc:
if self.read_ref(branch) == new_oid:
return new_oid
raise AuthorityGitError("AUTHORITY_BRANCH_CREATE_CONFLICT") from exc
readback = self.read_ref(branch)
if readback != new_oid:
raise AuthorityGitError("AUTHORITY_CAS_READBACK_MISMATCH")
return readback
def ff_CAS_main_after_verified_protected_merge(self, expected_oid: str, new_oid: str) -> str:
_oid(expected_oid, "expected_oid")
_oid(new_oid, "new_oid")
@@ -124,7 +124,7 @@ class GovernedRuntime:
workspace = DetachedGitWorkspace(worktree_path)
if workspace.head_oid() != lease.expected_branch_oid or not workspace.is_detached(): raise RuntimeError("WORKTREE_HEAD_MISMATCH")
job_root = self._job_root(lease.task_id); controller = GitOpsJobController(JobStore(job_root, payload_root=job_root / "payloads")); credential = ServerSideCredentialProjection(self.config.ssh_key_path, self.config.token_path); remote = GiteaContinuationRemote(workspace=worktree_path, credential=credential, remote_name=self.config.remote_name, repository=lease.repository, api_base=self.config.api_base); executor = ContinuationExecutor(controller, AuthorityGitAdapter(self.authority), workspace, remote, credential)
binding = ContinuationRuntimeBinding(request=lease, workspace_path=worktree_path, lease_not_before=datetime.now(timezone.utc) - timedelta(seconds=1), lease_expires_at=datetime.now(timezone.utc) + timedelta(seconds=lease.lease_seconds), original_receipt_valid=True, original_grant_consumed=True, permissions=ContinuationPermissions())
binding = ContinuationRuntimeBinding(request=lease, workspace_path=worktree_path, lease_not_before=datetime.now(timezone.utc) - timedelta(seconds=1), lease_expires_at=datetime.now(timezone.utc) + timedelta(seconds=lease.lease_seconds), original_receipt_valid=True, original_grant_consumed=True, permissions=ContinuationPermissions(create_target_branch=lease.branch_mode == "CREATE_NEW"))
result = (binding, controller, executor); self.runtimes[key] = result; self.controllers[lease.task_id] = controller; return result
def create_task(self, request: Mapping[str, Any]) -> dict[str, Any]:
@@ -148,7 +148,16 @@ class GovernedRuntime:
if request["target_branch"] != binding.request.target_branch or request["expected_branch_oid"] != binding.request.expected_branch_oid: raise RuntimeError("LEASE_BINDING_CONFLICT")
commit = ContinuationCommit(idempotency_key=str(request["idempotency_key"]), files=self._files(request), message=str(request["message"]))
executor.workspace.materialize_files(commit.files)
return executor.execute(binding, commit)
result = executor.execute(binding, commit)
receipt = result.get("receipt") if isinstance(result, Mapping) else None
receipt = receipt if isinstance(receipt, Mapping) else {}
commit_oid = receipt.get("authority_oid") or receipt.get("desired_remote_oid")
if not isinstance(commit_oid, str) or len(commit_oid) != 40 or any(ch not in "0123456789abcdef" for ch in commit_oid):
raise RuntimeError("COMMIT_RECEIPT_OID_MISSING")
response = dict(result)
response["commit_oid"] = commit_oid
response["commit_visible"] = bool(receipt.get("commit_visible") or receipt.get("remote_verified"))
return response
def _controller_for_job(self, job_id: str) -> GitOpsJobController:
for controller in self.controllers.values():
@@ -52,11 +52,12 @@ class AuthorityPort(Protocol):
def branch_head(self, branch: str) -> str: ...
def is_ancestor(self, older: str, newer: str) -> bool: ...
def compare_and_swap_branch(self, branch: str, expected: str, new: str) -> str: ...
def create_task_branch_CAS(self, branch: str, new: str) -> str: ...
class RemotePort(Protocol):
def repository_identity(self) -> str: ...
def push_exact(self, branch: str, expected: str, new: str) -> None: ...
def push_exact(self, branch: str, expected: str | None, new: str) -> None: ...
def read_git_ref(self, branch: str) -> str | None: ...
def read_api_branch(self, branch: str) -> str | None: ...
def commit_visible(self, oid: str) -> bool: ...
@@ -106,6 +107,9 @@ class AuthorityGitAdapter:
def compare_and_swap_branch(self, branch: str, expected: str, new: str) -> str:
return self.executor.advance_task_branch_CAS(self._ref(branch), expected, new)
def create_task_branch_CAS(self, branch: str, new: str) -> str:
return self.executor.create_task_branch_CAS(self._ref(branch), new)
@dataclass(slots=True)
class DetachedGitWorkspace:
@@ -258,10 +262,16 @@ class GiteaContinuationRemote:
raise ContinuationExecutionError("GITEA_GIT_READBACK_INVALID")
return oid
def push_exact(self, branch: str, expected: str, new: str) -> None:
def push_exact(self, branch: str, expected: str | None, new: str) -> None:
self._require_projected()
self.repository_identity()
if self.read_git_ref(branch) != expected:
observed = self.read_git_ref(branch)
if observed == new:
return
if expected is None:
if observed is not None:
raise ContinuationExecutionError("GITEA_BRANCH_EXPECTATION_MISMATCH")
elif observed != expected:
raise ContinuationExecutionError("GITEA_BRANCH_EXPECTATION_MISMATCH")
_run(["git", "push", self.remote_name, f"{new}:refs/heads/{branch}"], cwd=self.workspace)
@@ -321,8 +331,18 @@ class ContinuationPermissions:
production_write: bool = False
main_write: bool = False
def validate(self) -> None:
if any(asdict(self).values()):
def validate(self, *, branch_mode: str) -> None:
if branch_mode not in {"CREATE_NEW", "CONTINUE_EXISTING"}:
raise ContinuationExecutionError("BRANCH_MODE_INVALID")
expected_create = branch_mode == "CREATE_NEW"
forbidden = (
self.force_push,
self.tag_write,
self.baseline_write,
self.production_write,
self.main_write,
)
if self.create_target_branch != expected_create or any(forbidden):
raise ContinuationExecutionError("MUTATION_PERMISSION_DENIED")
@@ -415,12 +435,14 @@ class ContinuationExecutor:
def _validate_preflight(self, binding: ContinuationRuntimeBinding) -> None:
request = binding.request
normalized = validate_writer_lease_request_v2(self._request_mapping(request), self.authority)
if normalized != {**self._request_mapping(request), "create_target_branch": False, "worktree_mode": "detached_at_expected_head", "cas_ref_update_required": True}:
raise ContinuationExecutionError("CONTINUATION_NORMALIZATION_INVALID")
# WriterLeaseRequestV2 validation is the single normalization authority.
# CREATE_NEW legitimately normalizes to create_target_branch=True and
# detached_at_base_oid; pinning the CONTINUE_EXISTING shape here rejects
# valid first commits before the governed CAS ref update can run.
validate_writer_lease_request_v2(self._request_mapping(request), self.authority)
if not binding.original_receipt_valid or not binding.original_grant_consumed:
raise ContinuationExecutionError("ORIGINAL_GRANT_LINEAGE_INVALID")
binding.permissions.validate()
binding.permissions.validate(branch_mode=request.branch_mode)
now = self.now()
if now < binding.lease_not_before:
raise ContinuationExecutionError("LEASE_NOT_ACTIVE")
@@ -442,7 +464,7 @@ class ContinuationExecutor:
"campaign_id": request.task_id,
"task_id": request.task_id,
"repository": request.repository,
"operation": "CONTINUATION_COMMIT_FILES",
"operation": "CONTINUATION_COMMIT_FILES_CREATE_NEW" if request.branch_mode == "CREATE_NEW" else "CONTINUATION_COMMIT_FILES",
"branch": request.target_branch,
"expected_branch_oid": request.expected_branch_oid,
"base_oid": request.base_oid,
@@ -488,7 +510,10 @@ class ContinuationExecutor:
new_oid = self.workspace.commit_files(commit.files, commit.message)
if not OID_RE.fullmatch(new_oid) or not self.authority.is_ancestor(request.expected_branch_oid, new_oid):
raise ContinuationExecutionError("NON_FAST_FORWARD_COMMIT")
authority_oid = self.authority.compare_and_swap_branch(request.target_branch, request.expected_branch_oid, new_oid)
if request.branch_mode == "CREATE_NEW":
authority_oid = self.authority.create_task_branch_CAS(request.target_branch, new_oid)
else:
authority_oid = self.authority.compare_and_swap_branch(request.target_branch, request.expected_branch_oid, new_oid)
if authority_oid != new_oid:
raise ContinuationExecutionError("AUTHORITY_CAS_READBACK_MISMATCH")
self.controller.advance(
@@ -499,7 +524,7 @@ class ContinuationExecutor:
)
try:
self.controller.advance(submitted.job_id, JobState.REMOTE_SYNCING, side_effect_state="IN_PROGRESS")
self.remote.push_exact(request.target_branch, request.expected_branch_oid, new_oid)
self.remote.push_exact(request.target_branch, None if request.branch_mode == "CREATE_NEW" else request.expected_branch_oid, new_oid)
except Exception:
return self._remote_pending(submitted.job_id, authority_oid, new_oid)
return self._finish_remote_verification(submitted.job_id, authority_oid, new_oid)
@@ -575,7 +600,7 @@ class ContinuationExecutor:
if authority_oid == git_oid == api_oid == new_oid and visible:
self.controller.advance(job_id, JobState.RECONCILING, side_effect_state="OBSERVATION_FIRST")
return self._finish_remote_verification(job_id, new_oid, new_oid)
expected_oid = request["expected_branch_oid"]
expected_oid = None if request.get("branch_mode") == "CREATE_NEW" else request["expected_branch_oid"]
if (
self.authority.is_ancestor(new_oid, authority_oid)
and git_oid is not None
@@ -0,0 +1,73 @@
from __future__ import annotations
import inspect
from aios_authority_git_executor import AuthorityGitExecutor
from aios_continuation_executor import ContinuationExecutor, ContinuationPermissions
from aios_continuation_lease import WriterLeaseRequestV2
from authorized_task_writer_provisioning import validate_writer_lease_request_v2
BASE = "a" * 40
BRANCH = "agent/bridge-create-new-normalization-test"
class LocalAuthority:
def __init__(self, branch_head: str | None) -> None:
self._branch_head = branch_head
def branch_exists(self, branch: str) -> bool:
return self._branch_head is not None
def branch_head(self, branch: str) -> str:
assert self._branch_head is not None
return self._branch_head
def commit_exists(self, oid: str) -> bool:
return oid == BASE
def is_ancestor(self, older: str, newer: str) -> bool:
return older == BASE and newer == BASE
def request(branch_mode: str) -> dict[str, object]:
return {
"schema": WriterLeaseRequestV2.schema,
"task_id": "bridge-create-new-normalization-test",
"repository": "aios/aios",
"branch_mode": branch_mode,
"base_oid": BASE,
"expected_branch_oid": BASE,
"target_branch": BRANCH,
"generation": 1,
"lease_seconds": 120,
}
def main() -> None:
create_new = validate_writer_lease_request_v2(request("CREATE_NEW"), LocalAuthority(None))
assert create_new["create_target_branch"] is True
assert create_new["worktree_mode"] == "detached_at_base_oid"
assert create_new["cas_ref_update_required"] is True
continue_existing = validate_writer_lease_request_v2(request("CONTINUE_EXISTING"), LocalAuthority(BASE))
assert continue_existing["create_target_branch"] is False
assert continue_existing["worktree_mode"] == "detached_at_expected_head"
assert continue_existing["cas_ref_update_required"] is True
preflight = inspect.getsource(ContinuationExecutor._validate_preflight)
assert "validate_writer_lease_request_v2" in preflight
assert '"create_target_branch": False' not in preflight
assert '"worktree_mode": "detached_at_expected_head"' not in preflight
ContinuationPermissions(create_target_branch=True).validate(branch_mode="CREATE_NEW")
ContinuationPermissions().validate(branch_mode="CONTINUE_EXISTING")
cas = inspect.getsource(AuthorityGitExecutor.create_task_branch_CAS)
assert "update-ref" in cas
assert '"0" * 40' in cas
print("CREATE_NEW_NORMALIZATION_REGRESSION_PASS")
if __name__ == "__main__":
main()