feat: add optional ticket selection to planning report generation

Add a `numbers` form field to the planning report endpoint, allowing
admins to generate a report for a specific subset of open tickets
instead of always planning all open tickets. The selected numbers are
parsed from a comma-separated string, deduplicated, and passed to the
background job. The job's scope is recorded in the audit log and the
Devii tool description is updated to document the new parameter.
This commit is contained in:
2026-06-19 12:25:51 +00:00
parent ff3cbbbfe2
commit 1350e7fc66
16 changed files with 494 additions and 39 deletions
+83 -2
View File
@@ -14,7 +14,12 @@ from devplacepy.services.gitea import runtime, store
from devplacepy.services.gitea.config import gitea_config
from devplacepy.services.gitea.enhance import enhance_ticket
from devplacepy.services.gitea.fake import FakeGiteaClient
from devplacepy.services.gitea.planning import generate_plan
from devplacepy.services.gitea.planning import (
collect_open_issues,
generate_plan,
verbatim_tickets,
)
from devplacepy.services.jobs.planning_service import PlanningReportService
from devplacepy.services.gitea.service import IssueTrackerService
from devplacepy.services.jobs import queue
from devplacepy.services.jobs.issue_create_service import IssueCreateService
@@ -324,11 +329,87 @@ def test_planning_meters_usage_via_gateway(gitea_env, monkeypatch):
issues = [{"number": 1, "title": "t", "labels": [], "body": "b"}]
markdown, ai_used = run_async(generate_plan(issues, gitea_config(), totals))
assert ai_used is True
assert markdown == "# Open Tickets Implementation Plan\n\nDone."
assert markdown.startswith("# Open Tickets Implementation Plan\n\nDone.")
assert "# Appendix: Source Tickets (verbatim)" in markdown
assert totals["calls"] == 1
assert totals["total_tokens"] == 600
def test_planning_ai_output_always_carries_verbatim_appendix(gitea_env, monkeypatch):
set_setting("issue_ai_enhance", "1")
refresh_snapshot()
monkeypatch.setattr(
"devplacepy.stealth.stealth_async_client",
_fake_gateway("# Open Tickets Implementation Plan\n\nSummarised plan only."),
)
issues = [
{
"number": 12,
"title": "Add export",
"labels": [{"name": "feature"}],
"html_url": "https://gitea.test/retoor/pydevplace/issues/12",
"body": "Full body line A.\nFull body line B.",
}
]
markdown, ai_used = run_async(generate_plan(issues, gitea_config()))
assert ai_used is True
assert "Summarised plan only." in markdown
assert "# Appendix: Source Tickets (verbatim)" in markdown
assert "## #12 Add export" in markdown
assert "https://gitea.test/retoor/pydevplace/issues/12" in markdown
assert "> Full body line A." in markdown
assert "> Full body line B." in markdown
def test_verbatim_tickets_reproduces_full_body_and_labels():
issues = [
{
"number": 5,
"title": "Fix crash",
"labels": [{"name": "bug"}, {"name": "urgent"}],
"html_url": "https://gitea.test/x/5",
"body": "Steps:\n1. open\n2. boom",
}
]
markdown = verbatim_tickets(issues)
assert markdown.startswith("# Appendix: Source Tickets (verbatim)")
assert "## #5 Fix crash" in markdown
assert "**Labels**: bug, urgent" in markdown
assert "**Source**: https://gitea.test/x/5" in markdown
assert "> Steps:" in markdown
assert "> 1. open" in markdown
assert "> 2. boom" in markdown
def test_collect_open_issues_pages_through_fake(gitea_env):
for index in range(3):
run_async(gitea_env.create_issue(f"Ticket {index}", f"Body {index}"))
issues = run_async(collect_open_issues(gitea_env))
assert {issue["number"] for issue in issues} == {1, 2, 3}
def test_planning_service_filters_to_selected_numbers(gitea_env):
for index in range(3):
run_async(gitea_env.create_issue(f"Ticket {index}", f"Body {index}"))
service = PlanningReportService()
selected = run_async(service._collect([3, 1]))
assert [issue["number"] for issue in selected] == [3, 1]
missing = run_async(service._collect([99]))
assert missing == []
everything = run_async(service._collect([]))
assert {issue["number"] for issue in everything} == {1, 2, 3}
def test_planning_service_parses_payload_numbers():
service = PlanningReportService()
job = {"payload": {"numbers": ["4", 7, "x", 7, None]}}
assert service._selected_numbers(job) == [4, 7]
assert service._selected_numbers({"payload": {}}) == []
def test_enhance_meters_usage_via_gateway(gitea_env, monkeypatch):
set_setting("issue_ai_enhance", "1")
refresh_snapshot()