diff --git a/scripts/agentctl_console.py b/scripts/agentctl_console.py index a2461a92..ba2bf0bc 100644 --- a/scripts/agentctl_console.py +++ b/scripts/agentctl_console.py @@ -10,6 +10,7 @@ import argparse import contextlib import json import mimetypes +import os import re import tempfile import threading @@ -177,8 +178,9 @@ def integrate_prepare(repo: ac.Repo, args) -> dict: _register_integrate_task(integ_path, integrate_id, owner=task.get('owner') or 'Cursor') integ_repo = ac.Repo(str(integ_path)) integ_task = ac.task_row(integ_repo, integrate_id) - request_id = getattr(args, 'request_id', None) or f"{integrate_id}-prepare" + request_id = getattr(args, 'request_id', None) or f"{integrate_id}-prepare-{uuid.uuid4().hex[:8]}" msg = f"merge: candidate {args.task} into dev" + # New request_id when recovering after a failed fence-less attempt. with ac.Lease(integ_repo, integ_task, ['path:.', 'git:index', 'git:dev'], request_id, ac.digest(msg + expected + task_branch), kind='integrate_prepare') as lease: if lease.replay: @@ -191,12 +193,25 @@ def integrate_prepare(repo: ac.Repo, args) -> dict: 'head': head, 'expected_dev': expected, } - result = ac.git(integ_path, 'merge', '--no-ff', '-m', msg, task_branch, check=False) - if result.returncode != 0: + # Merge commit must run with AGENTCTL_* fence (pre-commit hook), same as integrate(). + env = os.environ.copy() + env.update( + AGENTCTL_RUN_ID=lease.request_id, + AGENTCTL_TASK_ID=integrate_id, + AGENTCTL_GENERATION=str(lease.row['generation']), + AGENTCTL_EPOCH=integ_repo.identity()['epoch'], + AGENTCTL_FENCE=str(lease.row.get('fence') or 0), + ) + code, orphan = ac.supervise( + integ_repo, lease, + ['git', 'merge', '--no-ff', '-m', msg, task_branch], + integ_path, env, + ) + if code or orphan: with contextlib.suppress(Exception): ac.git(integ_path, 'merge', '--abort', check=False) - lease.finish(1, True) - ac.deny('GIT_ERROR', 'merge --no-ff failed', stderr=(result.stderr or '')[:500]) + lease.finish(code or 1, True) + ac.deny('GIT_ERROR', 'merge --no-ff failed', exit_code=code, orphan=bool(orphan)) head = ac.git(integ_path, 'rev-parse', 'HEAD') first_parent = ac.git(integ_path, 'rev-parse', 'HEAD^1') if first_parent != expected: