Add backlog audit and task update command
- agents/prompts/backlog_audit.md: QA analyst prompt for checking
which pending tasks are already implemented in the codebase
- agents/runner.py: run_audit() — project-level agent that reads
all pending tasks, inspects code, returns classification
- cli/main.py: kin audit <project_id> — runs audit, offers to mark
done tasks; kin task update <id> --status --priority
- web/api.py: POST /api/projects/{id}/audit (runs audit inline),
POST /api/projects/{id}/audit/apply (batch mark as done)
- Frontend: "Audit backlog" button on ProjectView with results
modal showing already_done/still_pending/unclear categories
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e755a19633
commit
96509dcafc
9 changed files with 548 additions and 2 deletions
|
|
@ -6,7 +6,7 @@ import pytest
|
|||
from unittest.mock import patch, MagicMock
|
||||
from core.db import init_db
|
||||
from core import models
|
||||
from agents.runner import run_agent, run_pipeline, _try_parse_json
|
||||
from agents.runner import run_agent, run_pipeline, run_audit, _try_parse_json
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
@ -335,3 +335,82 @@ class TestNonInteractive:
|
|||
run_agent(conn, "debugger", "VDOL-001", "vdol", allow_write=False)
|
||||
cmd = mock_run.call_args[0][0]
|
||||
assert "--dangerously-skip-permissions" not in cmd
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# run_audit
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestRunAudit:
|
||||
@patch("agents.runner.subprocess.run")
|
||||
def test_audit_success(self, mock_run, conn):
|
||||
"""Audit should return parsed already_done/still_pending/unclear."""
|
||||
audit_output = json.dumps({
|
||||
"already_done": [{"id": "VDOL-001", "reason": "Fixed in runner.py"}],
|
||||
"still_pending": [],
|
||||
"unclear": [],
|
||||
})
|
||||
mock_run.return_value = _mock_claude_success({"result": audit_output})
|
||||
|
||||
result = run_audit(conn, "vdol")
|
||||
|
||||
assert result["success"] is True
|
||||
assert len(result["already_done"]) == 1
|
||||
assert result["already_done"][0]["id"] == "VDOL-001"
|
||||
|
||||
@patch("agents.runner.subprocess.run")
|
||||
def test_audit_logs_to_db(self, mock_run, conn):
|
||||
"""Audit should log to agent_logs with role=backlog_audit."""
|
||||
mock_run.return_value = _mock_claude_success({
|
||||
"result": json.dumps({"already_done": [], "still_pending": [], "unclear": []}),
|
||||
})
|
||||
|
||||
run_audit(conn, "vdol")
|
||||
|
||||
logs = conn.execute(
|
||||
"SELECT * FROM agent_logs WHERE agent_role='backlog_audit'"
|
||||
).fetchall()
|
||||
assert len(logs) == 1
|
||||
assert logs[0]["action"] == "audit"
|
||||
|
||||
def test_audit_no_pending_tasks(self, conn):
|
||||
"""If no pending tasks, return success with empty lists."""
|
||||
# Mark existing task as done
|
||||
models.update_task(conn, "VDOL-001", status="done")
|
||||
|
||||
result = run_audit(conn, "vdol")
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["already_done"] == []
|
||||
assert "No pending tasks" in result.get("message", "")
|
||||
|
||||
def test_audit_project_not_found(self, conn):
|
||||
result = run_audit(conn, "nonexistent")
|
||||
assert result["success"] is False
|
||||
assert "not found" in result["error"]
|
||||
|
||||
@patch("agents.runner.subprocess.run")
|
||||
def test_audit_uses_sonnet(self, mock_run, conn):
|
||||
"""Audit should use sonnet model."""
|
||||
mock_run.return_value = _mock_claude_success({
|
||||
"result": json.dumps({"already_done": [], "still_pending": [], "unclear": []}),
|
||||
})
|
||||
|
||||
run_audit(conn, "vdol")
|
||||
|
||||
cmd = mock_run.call_args[0][0]
|
||||
model_idx = cmd.index("--model")
|
||||
assert cmd[model_idx + 1] == "sonnet"
|
||||
|
||||
@patch("agents.runner.subprocess.run")
|
||||
def test_audit_includes_tasks_in_prompt(self, mock_run, conn):
|
||||
"""The prompt should contain the task title."""
|
||||
mock_run.return_value = _mock_claude_success({
|
||||
"result": json.dumps({"already_done": [], "still_pending": [], "unclear": []}),
|
||||
})
|
||||
|
||||
run_audit(conn, "vdol")
|
||||
|
||||
prompt = mock_run.call_args[0][0][2] # -p argument
|
||||
assert "VDOL-001" in prompt
|
||||
assert "Fix bug" in prompt
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue