feat: integrate Analyst into Plane/Orchestrator pipeline
- Add git fetch+checkout in agent launch cmd (ensures correct branch) - Add git fetch+checkout in _monitor_agent before commit/push - Post start comment in Plane when analyst launches - Post :approved: request comment after analyst completes successfully - Branch lookup moved before cmd construction for reuse
This commit is contained in:
@@ -8,7 +8,7 @@ from ..db import get_db, get_task_by_repo_branch, update_task_stage
|
||||
from ..stages import get_next_stage, get_qg_for_stage, get_agent_for_stage
|
||||
from ..qg.checks import QG_CHECKS
|
||||
from ..notifications import notify_stage_change, notify_qg_failure
|
||||
from ..plane_sync import notify_stage_change as plane_notify_stage
|
||||
from ..plane_sync import notify_stage_change as plane_notify_stage, add_comment as plane_add_comment
|
||||
|
||||
logger = logging.getLogger("orchestrator.launcher")
|
||||
|
||||
@@ -94,8 +94,12 @@ class AgentLauncher:
|
||||
system_prompt = config["system_prompt"]
|
||||
allowed_tools = config["allowed_tools"]
|
||||
|
||||
# Determine branch for checkout
|
||||
_br_row = get_db().execute("SELECT branch FROM tasks WHERE id=?", (task_id,)).fetchone() if task_id else None
|
||||
agent_branch = _br_row[0] if _br_row else "main"
|
||||
|
||||
cmd = (
|
||||
f'cd {local_repo_path} && '
|
||||
f'cd {local_repo_path} && git fetch origin 2>/dev/null; git checkout {agent_branch} 2>/dev/null || git checkout -b {agent_branch} origin/{agent_branch} 2>/dev/null; '
|
||||
f'{self.CLAUDE_BIN} --print '
|
||||
f'"$(cat {task_file})" '
|
||||
f'--system-prompt "$(cat {system_prompt})" '
|
||||
@@ -137,8 +141,7 @@ class AgentLauncher:
|
||||
t.start()
|
||||
|
||||
# Start monitor thread (waits for completion, commits, pushes)
|
||||
task_row = get_db().execute("SELECT branch FROM tasks WHERE id=?", (task_id,)).fetchone() if task_id else None
|
||||
agent_branch = task_row[0] if task_row else "main"
|
||||
# agent_branch already computed above
|
||||
m = threading.Thread(
|
||||
target=self._monitor_agent,
|
||||
args=(proc, run_id, agent, repo, agent_branch),
|
||||
@@ -193,6 +196,20 @@ class AgentLauncher:
|
||||
"GIT_COMMITTER_NAME": "claude-bot",
|
||||
"GIT_COMMITTER_EMAIL": "claude-bot@mva154.local",
|
||||
}
|
||||
# Checkout feature branch before committing
|
||||
subprocess.run(
|
||||
["git", "-C", repo_path, "fetch", "origin"],
|
||||
capture_output=True, text=True, timeout=30, env=git_env
|
||||
)
|
||||
checkout_result = subprocess.run(
|
||||
["git", "-C", repo_path, "checkout", branch],
|
||||
capture_output=True, text=True, timeout=30, env=git_env
|
||||
)
|
||||
if checkout_result.returncode != 0:
|
||||
subprocess.run(
|
||||
["git", "-C", repo_path, "checkout", "-b", branch, f"origin/{branch}"],
|
||||
capture_output=True, text=True, timeout=30, env=git_env
|
||||
)
|
||||
result = subprocess.run(
|
||||
["git", "-C", repo_path, "status", "--porcelain"],
|
||||
capture_output=True, text=True, timeout=10, env=git_env
|
||||
@@ -258,7 +275,18 @@ class AgentLauncher:
|
||||
if qg_name and qg_name in QG_CHECKS:
|
||||
check_fn = QG_CHECKS[qg_name]
|
||||
if qg_name in ("check_review_approved", "check_analysis_approved"):
|
||||
# Skip — requires human approval (handled by webhook comment handler)
|
||||
# Requires human approval - post request comment if analyst just finished
|
||||
if agent == "analyst" and qg_name == "check_analysis_approved" and work_item_id:
|
||||
files_check = QG_CHECKS.get("check_analysis_complete")
|
||||
if files_check:
|
||||
files_ok, _ = files_check(repo, work_item_id)
|
||||
if files_ok:
|
||||
plane_add_comment(
|
||||
work_item_id,
|
||||
"📋 BRD/ТЗ/AC/TestPlan готовы. "
|
||||
"Прошу review и реакцию :approved: для продвижения в Architecture."
|
||||
)
|
||||
logger.info(f"Task {task_id}: analyst finished, requested :approved: in Plane")
|
||||
return
|
||||
elif qg_name == "check_ci_green":
|
||||
passed, reason = check_fn(repo, branch)
|
||||
|
||||
@@ -28,7 +28,8 @@ def init_db():
|
||||
stage TEXT DEFAULT 'created',
|
||||
agent_running TEXT,
|
||||
created_at TEXT DEFAULT (datetime('now')),
|
||||
updated_at TEXT DEFAULT (datetime('now'))
|
||||
updated_at TEXT DEFAULT (datetime('now')),
|
||||
plane_issue_id TEXT
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS agent_runs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
||||
@@ -130,6 +130,9 @@ async def handle_work_item_created(data: dict):
|
||||
task_desc = f"Work item: {work_item_id}\nRepo: {repo}\nBranch: {branch}\nStage: analysis\nTitle: {name}"
|
||||
run_id = launcher.launch("analyst", repo, task_desc, task_id=task_id)
|
||||
logger.info(f"Task {task_id}: launched analyst (run_id={run_id})")
|
||||
# Post start comment to Plane
|
||||
from ..plane_sync import add_comment as _add_comment
|
||||
_add_comment(work_item_id, "🔍 Analyst запущен. BRD/ТЗ/AC/TestPlan в работе (ожидайте 8-15 мин).")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to launch analyst for {work_item_id}: {e}")
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ from unittest.mock import patch, MagicMock, AsyncMock
|
||||
# Override DB path before importing app
|
||||
_test_db = os.path.join(tempfile.gettempdir(), "test_orchestrator.db")
|
||||
os.environ["ORCH_DB_PATH"] = _test_db
|
||||
os.environ["ORCH_PLANE_WEBHOOK_SECRET"] = ""
|
||||
os.environ["ORCH_GITEA_WEBHOOK_SECRET"] = ""
|
||||
os.environ["ORCH_REPOS_DIR"] = tempfile.gettempdir()
|
||||
os.environ["ORCH_HOST_REPOS_DIR"] = "/home/slin/repos"
|
||||
os.environ["ORCH_GITEA_TOKEN"] = "test-token"
|
||||
|
||||
Reference in New Issue
Block a user