kin: KIN-045 добавить в GUI третью кнопку Revise (🔄) рядом с Approve/Reject. Revise = вернуть задачу агенту с комментарием человека. Модалка с textarea 'что доисследовать/доработать'. Задача возвращается в in_progress, агент получает свой предыдущий output + комментарий директора и дорабатывает
This commit is contained in:
parent
4fd825dc58
commit
01c39cc45c
3 changed files with 147 additions and 0 deletions
|
|
@ -168,6 +168,19 @@ def test_revise_not_found(client):
|
||||||
assert r.status_code == 404
|
assert r.status_code == 404
|
||||||
|
|
||||||
|
|
||||||
|
def test_revise_task_response_includes_comment(client):
|
||||||
|
"""Ответ /revise содержит поле comment с переданным текстом."""
|
||||||
|
r = client.post("/api/tasks/P1-001/revise", json={"comment": "Уточни требования"})
|
||||||
|
assert r.status_code == 200
|
||||||
|
assert r.json()["comment"] == "Уточни требования"
|
||||||
|
|
||||||
|
|
||||||
|
def test_revise_task_missing_comment_returns_422(client):
|
||||||
|
"""Запрос /revise без поля comment → 422 Unprocessable Entity (Pydantic validation)."""
|
||||||
|
r = client.post("/api/tasks/P1-001/revise", json={})
|
||||||
|
assert r.status_code == 422
|
||||||
|
|
||||||
|
|
||||||
def test_task_pipeline_not_found(client):
|
def test_task_pipeline_not_found(client):
|
||||||
r = client.get("/api/tasks/NOPE/pipeline")
|
r = client.get("/api/tasks/NOPE/pipeline")
|
||||||
assert r.status_code == 404
|
assert r.status_code == 404
|
||||||
|
|
|
||||||
|
|
@ -161,3 +161,107 @@ class TestLanguageInProject:
|
||||||
def test_context_carries_language(self, conn):
|
def test_context_carries_language(self, conn):
|
||||||
ctx = build_context(conn, "VDOL-001", "pm", "vdol")
|
ctx = build_context(conn, "VDOL-001", "pm", "vdol")
|
||||||
assert ctx["project"]["language"] == "ru"
|
assert ctx["project"]["language"] == "ru"
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# KIN-045: Revise context — revise_comment + last agent output injection
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class TestReviseContext:
|
||||||
|
"""build_context и format_prompt корректно инжектируют контекст ревизии."""
|
||||||
|
|
||||||
|
def test_build_context_includes_revise_comment_in_task(self, conn):
|
||||||
|
"""Если у задачи есть revise_comment, он попадает в ctx['task']."""
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE tasks SET revise_comment=? WHERE id='VDOL-001'",
|
||||||
|
("Доисследуй edge case с пустым массивом",),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
ctx = build_context(conn, "VDOL-001", "backend_dev", "vdol")
|
||||||
|
assert ctx["task"]["revise_comment"] == "Доисследуй edge case с пустым массивом"
|
||||||
|
|
||||||
|
def test_build_context_fetches_last_agent_output_when_revise_comment_set(self, conn):
|
||||||
|
"""При revise_comment build_context достаёт last_agent_output из agent_logs."""
|
||||||
|
from core import models
|
||||||
|
models.log_agent_run(
|
||||||
|
conn, "vdol", "developer", "execute",
|
||||||
|
task_id="VDOL-001",
|
||||||
|
output_summary="Реализован endpoint POST /api/items",
|
||||||
|
success=True,
|
||||||
|
)
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE tasks SET revise_comment=? WHERE id='VDOL-001'",
|
||||||
|
("Добавь валидацию входных данных",),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
ctx = build_context(conn, "VDOL-001", "backend_dev", "vdol")
|
||||||
|
assert ctx.get("last_agent_output") == "Реализован endpoint POST /api/items"
|
||||||
|
|
||||||
|
def test_build_context_no_last_agent_output_when_no_successful_logs(self, conn):
|
||||||
|
"""revise_comment есть, но нет успешных логов — last_agent_output отсутствует."""
|
||||||
|
from core import models
|
||||||
|
models.log_agent_run(
|
||||||
|
conn, "vdol", "developer", "execute",
|
||||||
|
task_id="VDOL-001",
|
||||||
|
output_summary="Permission denied",
|
||||||
|
success=False,
|
||||||
|
)
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE tasks SET revise_comment=? WHERE id='VDOL-001'",
|
||||||
|
("Повтори без ошибок",),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
ctx = build_context(conn, "VDOL-001", "backend_dev", "vdol")
|
||||||
|
assert "last_agent_output" not in ctx
|
||||||
|
|
||||||
|
def test_build_context_no_revise_fields_when_no_revise_comment(self, conn):
|
||||||
|
"""Обычная задача без revise_comment не получает last_agent_output в контексте."""
|
||||||
|
from core import models
|
||||||
|
models.log_agent_run(
|
||||||
|
conn, "vdol", "developer", "execute",
|
||||||
|
task_id="VDOL-001",
|
||||||
|
output_summary="Всё готово",
|
||||||
|
success=True,
|
||||||
|
)
|
||||||
|
# revise_comment не устанавливаем
|
||||||
|
ctx = build_context(conn, "VDOL-001", "backend_dev", "vdol")
|
||||||
|
assert "last_agent_output" not in ctx
|
||||||
|
assert ctx["task"].get("revise_comment") is None
|
||||||
|
|
||||||
|
def test_format_prompt_includes_director_revision_request(self, conn):
|
||||||
|
"""format_prompt содержит секцию '## Director's revision request:' при revise_comment."""
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE tasks SET revise_comment=? WHERE id='VDOL-001'",
|
||||||
|
("Обработай случай пустого списка",),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
ctx = build_context(conn, "VDOL-001", "backend_dev", "vdol")
|
||||||
|
prompt = format_prompt(ctx, "backend_dev", "You are a developer.")
|
||||||
|
assert "## Director's revision request:" in prompt
|
||||||
|
assert "Обработай случай пустого списка" in prompt
|
||||||
|
|
||||||
|
def test_format_prompt_includes_previous_output_before_revision(self, conn):
|
||||||
|
"""format_prompt содержит '## Your previous output (before revision):' при last_agent_output."""
|
||||||
|
from core import models
|
||||||
|
models.log_agent_run(
|
||||||
|
conn, "vdol", "developer", "execute",
|
||||||
|
task_id="VDOL-001",
|
||||||
|
output_summary="Сделал миграцию БД",
|
||||||
|
success=True,
|
||||||
|
)
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE tasks SET revise_comment=? WHERE id='VDOL-001'",
|
||||||
|
("Ещё добавь индекс",),
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
ctx = build_context(conn, "VDOL-001", "backend_dev", "vdol")
|
||||||
|
prompt = format_prompt(ctx, "backend_dev", "You are a developer.")
|
||||||
|
assert "## Your previous output (before revision):" in prompt
|
||||||
|
assert "Сделал миграцию БД" in prompt
|
||||||
|
|
||||||
|
def test_format_prompt_no_revision_sections_when_no_revise_comment(self, conn):
|
||||||
|
"""Без revise_comment в prompt нет секций ревизии."""
|
||||||
|
ctx = build_context(conn, "VDOL-001", "backend_dev", "vdol")
|
||||||
|
prompt = format_prompt(ctx, "backend_dev", "You are a developer.")
|
||||||
|
assert "## Director's revision request:" not in prompt
|
||||||
|
assert "## Your previous output (before revision):" not in prompt
|
||||||
|
|
|
||||||
|
|
@ -1412,6 +1412,36 @@ class TestCompletionMode:
|
||||||
"KIN-063: project-level 'review' должен применяться когда задача не имеет override"
|
"KIN-063: project-level 'review' должен применяться когда задача не имеет override"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@patch("core.followup.generate_followups")
|
||||||
|
@patch("agents.runner.run_hooks")
|
||||||
|
@patch("agents.runner.subprocess.run")
|
||||||
|
def test_auto_complete_not_broken_by_revise_comment(self, mock_run, mock_hooks, mock_followup, conn):
|
||||||
|
"""Регрессия KIN-045: revise_comment в задаче не ломает auto_complete flow.
|
||||||
|
|
||||||
|
Задача прошла ревизию (revise_comment != None, status=in_progress),
|
||||||
|
затем повторно запускается пайплайн в auto_complete режиме.
|
||||||
|
Последний шаг — tester → задача должна получить status='done'.
|
||||||
|
"""
|
||||||
|
mock_run.return_value = _mock_claude_success({"result": "all tests pass"})
|
||||||
|
mock_hooks.return_value = []
|
||||||
|
mock_followup.return_value = {"created": [], "pending_actions": []}
|
||||||
|
|
||||||
|
models.update_project(conn, "vdol", execution_mode="auto_complete")
|
||||||
|
models.update_task(
|
||||||
|
conn, "VDOL-001",
|
||||||
|
status="in_progress",
|
||||||
|
revise_comment="Добавь тест для пустого массива",
|
||||||
|
)
|
||||||
|
|
||||||
|
steps = [{"role": "developer", "brief": "fix"}, {"role": "tester", "brief": "test"}]
|
||||||
|
result = run_pipeline(conn, "VDOL-001", steps)
|
||||||
|
|
||||||
|
assert result["success"] is True
|
||||||
|
task = models.get_task(conn, "VDOL-001")
|
||||||
|
assert task["status"] == "done", (
|
||||||
|
"KIN-045: revise_comment не должен мешать auto_complete авто-завершению"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# KIN-048: _run_autocommit — флаг, git path, env=
|
# KIN-048: _run_autocommit — флаг, git path, env=
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue