# retoor from .._shared import endpoint, field GROUP = { "slug": "issues", "title": "Issue Reports", "intro": """ # Issue Reports The issue tracker is a full integration with a Gitea repository. The listing and detail views read issues straight from Gitea with their live status, and a report you file is first rewritten by the internal AI service into a consistent ticket, then posted to Gitea as an issue. The original reporter is notified when a developer replies or the status changes, and a comment posted here is pushed to Gitea and attributed to your account. Every endpoint follows the shared [Conventions & Errors](/docs/conventions.html) (auth, content negotiation, pagination, status codes); see [Authentication](/docs/authentication.html) for the four ways to sign requests. """, "endpoints": [ endpoint( id="issues-list", method="GET", path="/issues", title="List issue tickets", summary="Render the issue board from Gitea, paginated and filterable by state.", auth="public", interactive=True, params=[ field( "state", "query", "string", False, "open", "Filter: open (default), closed, or all.", ), field("page", "query", "integer", False, "1", "1-based page."), ], ), endpoint( id="issues-create", method="POST", path="/issues/create", title="Report an issue", summary="Enqueue an issue report. It is enhanced by AI and filed on the tracker.", auth="user", encoding="form", ajax=True, destructive=True, params=[ field( "title", "form", "string", True, "Login button misaligned", "Title, 1-200 characters.", ), field( "description", "form", "textarea", True, "Steps to reproduce...", "Description, 1-5000 characters.", ), ], notes=["Returns a job uid and status_url. Poll the status_url until status is done to get the issue number."], sample_response={ "uid": "JOB_UID", "status_url": "/issues/jobs/JOB_UID", }, ), endpoint( id="issues-job", method="GET", path="/issues/jobs/{uid}", title="Issue filing job status", summary="Poll the filing job; the result carries the new issue number and url.", auth="public", ajax=True, params=[field("uid", "path", "string", True, "JOB_UID", "Job uid.")], sample_response={ "uid": "JOB_UID", "kind": "issue_create", "status": "done", "number": 42, "issue_url": "/issues/42", "enhanced": True, "error": None, "created_at": "2026-06-12T09:00:00+00:00", "completed_at": "2026-06-12T09:00:03+00:00", }, ), endpoint( id="issues-planning-queue", method="POST", path="/issues/planning", title="Generate a tickets planning report", summary="Enqueue a phased markdown implementation document for open tickets, with each ticket's full description reproduced verbatim (inline plus a Source Tickets appendix) alongside implementation steps and acceptance criteria. Admin only.", auth="admin", encoding="form", ajax=True, params=[field("numbers", "form", "string", False, "1,4,7", "Comma-separated issue numbers to include. Omit to plan every open ticket.")], notes=["Returns a job uid and status_url. Poll the status_url until status is done to read the markdown and download it.", "Provide 'numbers' to plan only a chosen subset of open tickets; omitting it plans all open tickets."], sample_response={ "uid": "PLANNING_JOB_UID", "status_url": "/issues/planning/PLANNING_JOB_UID", }, ), endpoint( id="issues-planning-status", method="GET", path="/issues/planning/{uid}", title="Planning report job status", summary="Poll the planning job; the result carries the rendered markdown and the download URL. Admin only.", auth="admin", ajax=True, params=[field("uid", "path", "string", True, "PLANNING_JOB_UID", "Planning job uid.")], sample_response={ "uid": "PLANNING_JOB_UID", "kind": "planning", "status": "done", "download_url": "/issues/planning/PLANNING_JOB_UID/download", "markdown": "# Open Tickets Implementation Plan\n\n...", "ai_used": True, "issue_count": 12, "bytes_out": 4096, "error": None, "created_at": "2026-06-15T09:00:00+00:00", "completed_at": "2026-06-15T09:00:05+00:00", }, ), endpoint( id="issues-planning-download", method="GET", path="/issues/planning/{uid}/download", title="Download the planning report", summary="Download the generated planning report as a markdown file. Admin only.", auth="admin", interactive=True, params=[field("uid", "path", "string", True, "PLANNING_JOB_UID", "Planning job uid.")], ), endpoint( id="issues-detail", method="GET", path="/issues/{number}", title="View an issue ticket", summary="Render a Gitea issue and its comments. Returns an HTML page.", auth="public", interactive=True, params=[ field("number", "path", "integer", True, "12", "Issue number.") ], ), endpoint( id="issues-comment", method="POST", path="/issues/{number}/comment", title="Comment on an issue", summary="Post a comment to the Gitea issue, attributed to the current user.", auth="user", encoding="form", destructive=True, params=[ field("number", "path", "integer", True, "12", "Issue number."), field( "body", "form", "textarea", True, "I can reproduce this on mobile.", "Comment, 1-5000 characters.", ), ], ), endpoint( id="issues-status", method="POST", path="/issues/{number}/status", title="Change an issue status", summary="Open or close the Gitea issue. Admin only.", auth="admin", encoding="form", destructive=True, params=[ field("number", "path", "integer", True, "12", "Issue number."), field( "status", "form", "string", True, "closed", "New status: open or closed.", ), ], ), endpoint( id="issues-attachments-list", method="GET", path="/issues/{number}/attachments", title="List issue attachments", summary="Return the files attached to an issue ticket.", auth="public", params=[ field("number", "path", "integer", True, "12", "Issue number."), ], ), endpoint( id="issues-attachments-add", method="POST", path="/issues/{number}/attachments", title="Attach files to an issue", summary=( "Link already-uploaded files (from /uploads/upload) to an open issue. The " "files are also mirrored to the Gitea tracker. Allowed only while the issue is open." ), auth="user", encoding="form", destructive=True, params=[ field("number", "path", "integer", True, "12", "Issue number."), field( "attachment_uids", "form", "string", True, "a1b2c3d4,e5f6g7h8", "Comma separated attachment uids returned by /uploads/upload.", ), ], notes=[ "Only the open issue accepts changes; a closed issue returns 409.", "You can only link your own uploads unless you are an administrator.", ], ), endpoint( id="issues-attachments-delete", method="DELETE", path="/issues/{number}/attachments/{uid}", title="Delete an issue attachment", summary=( "Soft-delete a file from an open issue (owner or administrator) and remove it " "from the tracker." ), auth="user", destructive=True, params=[ field("number", "path", "integer", True, "12", "Issue number."), field("uid", "path", "string", True, "a1b2c3d4", "Attachment uid."), ], ), endpoint( id="issues-comment-attachments-add", method="POST", path="/issues/{number}/comments/{cid}/attachments", title="Attach files to an issue comment", summary=( "Link already-uploaded files to an issue comment (issue must be open). Mirrored " "to the Gitea comment." ), auth="user", encoding="form", destructive=True, params=[ field("number", "path", "integer", True, "12", "Issue number."), field("cid", "path", "integer", True, "1", "Gitea comment id."), field( "attachment_uids", "form", "string", True, "a1b2c3d4", "Comma separated attachment uids returned by /uploads/upload.", ), ], ), endpoint( id="issues-comment-attachments-delete", method="DELETE", path="/issues/{number}/comments/{cid}/attachments/{uid}", title="Delete an issue comment attachment", summary=( "Soft-delete a file from an issue comment (owner or administrator) and remove it " "from the tracker." ), auth="user", destructive=True, params=[ field("number", "path", "integer", True, "12", "Issue number."), field("cid", "path", "integer", True, "1", "Gitea comment id."), field("uid", "path", "string", True, "a1b2c3d4", "Attachment uid."), ], ), ], }