kin: KIN-021 Аудит-лог для --dangerously-skip-permissions в auto mode

This commit is contained in:
Gros Frumos 2026-03-16 07:13:32 +02:00
parent 67071c757d
commit a0b0976d8d
16 changed files with 1477 additions and 14 deletions

View file

@ -1557,3 +1557,110 @@ class TestReviewModeExecutionMode:
assert row["execution_mode"] == "review", (
"Регрессия KIN-055: execution_mode должен быть 'review' в SQLite после pipeline"
)
# ---------------------------------------------------------------------------
# KIN-021: Audit log for --dangerously-skip-permissions
# ---------------------------------------------------------------------------
class TestAuditLogDangerousSkip:
@patch("agents.runner._run_autocommit")
@patch("agents.runner._run_learning_extraction")
@patch("core.followup.generate_followups")
@patch("agents.runner.run_hooks")
@patch("agents.runner.subprocess.run")
def test_audit_log_written_on_permission_retry(
self, mock_run, mock_hooks, mock_followup, mock_learn, mock_autocommit, conn
):
"""При retry с --dangerously-skip-permissions записывается событие в audit_log."""
permission_fail = _mock_claude_failure("permission denied: cannot write file")
retry_success = _mock_claude_success({"result": "fixed"})
mock_run.side_effect = [permission_fail, retry_success]
mock_hooks.return_value = []
mock_followup.return_value = {"created": [], "pending_actions": []}
mock_learn.return_value = {"added": 0, "skipped": 0}
models.update_project(conn, "vdol", execution_mode="auto_complete")
steps = [{"role": "debugger", "brief": "find"}]
result = run_pipeline(conn, "VDOL-001", steps)
assert result["success"] is True
# Проверяем audit_log через прямой SQL
rows = conn.execute(
"SELECT * FROM audit_log WHERE task_id='VDOL-001'"
).fetchall()
assert len(rows) == 1
assert rows[0]["event_type"] == "dangerous_skip"
assert rows[0]["step_id"] == "debugger"
assert "debugger" in rows[0]["reason"]
@patch("agents.runner._run_autocommit")
@patch("agents.runner._run_learning_extraction")
@patch("core.followup.generate_followups")
@patch("agents.runner.run_hooks")
@patch("agents.runner.subprocess.run")
def test_dangerously_skipped_flag_set_on_task(
self, mock_run, mock_hooks, mock_followup, mock_learn, mock_autocommit, conn
):
"""tasks.dangerously_skipped=1 после retry с --dangerously-skip-permissions."""
permission_fail = _mock_claude_failure("permission denied: cannot write file")
retry_success = _mock_claude_success({"result": "fixed"})
mock_run.side_effect = [permission_fail, retry_success]
mock_hooks.return_value = []
mock_followup.return_value = {"created": [], "pending_actions": []}
mock_learn.return_value = {"added": 0, "skipped": 0}
models.update_project(conn, "vdol", execution_mode="auto_complete")
steps = [{"role": "debugger", "brief": "find"}]
run_pipeline(conn, "VDOL-001", steps)
# Верификация через прямой SQL (минуя ORM)
row = conn.execute(
"SELECT dangerously_skipped FROM tasks WHERE id='VDOL-001'"
).fetchone()
assert row is not None
assert row["dangerously_skipped"] == 1
@patch("agents.runner.run_hooks")
@patch("agents.runner.subprocess.run")
def test_no_audit_log_in_review_mode(self, mock_run, mock_hooks, conn):
"""В review mode retry не происходит, audit_log остаётся пустым."""
permission_fail = _mock_claude_failure("permission denied: cannot write file")
mock_run.return_value = permission_fail
mock_hooks.return_value = []
steps = [{"role": "debugger", "brief": "find"}]
result = run_pipeline(conn, "VDOL-001", steps)
assert result["success"] is False
rows = conn.execute(
"SELECT * FROM audit_log WHERE task_id='VDOL-001'"
).fetchall()
assert len(rows) == 0
@patch("agents.runner._run_autocommit")
@patch("agents.runner._run_learning_extraction")
@patch("core.followup.generate_followups")
@patch("agents.runner.run_hooks")
@patch("agents.runner.subprocess.run")
def test_audit_log_no_entry_on_normal_success(
self, mock_run, mock_hooks, mock_followup, mock_learn, mock_autocommit, conn
):
"""При успешном выполнении без retry audit_log не записывается."""
mock_run.return_value = _mock_claude_success({"result": "done"})
mock_hooks.return_value = []
mock_followup.return_value = {"created": [], "pending_actions": []}
mock_learn.return_value = {"added": 0, "skipped": 0}
models.update_project(conn, "vdol", execution_mode="auto_complete")
steps = [{"role": "tester", "brief": "test"}]
result = run_pipeline(conn, "VDOL-001", steps)
assert result["success"] is True
rows = conn.execute(
"SELECT * FROM audit_log WHERE task_id='VDOL-001'"
).fetchall()
assert len(rows) == 0