feat(palier-1): journal d'événements, détail d'issue, vieillissement, WIP

- board_events append-only + log_event/get_events/column_entry_times/
  get_metrics (débit, temps de cycle); journalisation des moves,
  créations, fermetures/rouvertures (UI et réconciliation Gitea),
  commentaires; un reclassement intra-colonne ne journalise pas
- client Gitea: get_issue, get_issue_comments (paginés), create_comment
- API: GET /api/issues/{o}/{r}/{n} (404 propre), POST .../comments,
  GET /api/events, GET /api/metrics, page /activity
- config: KANBANTE_WIP_LIMITS (colonne:limite) et KANBANTE_AGING_DAYS;
  le board calcule le temps en colonne (journal, replis updated_at
  puis création de l'issue)
- reconcile_board_state retourne aussi les numéros fermés/purgés
This commit is contained in:
Spark
2026-10-11 10:05:51 -04:00
parent 2060ca949d
commit a6d2ed9eae
4 changed files with 372 additions and 4 deletions
+23
View File
@@ -198,6 +198,29 @@ class GiteaClient:
issues_by_repo[full_name] = []
return issues_by_repo
async def get_issue(self, owner: str, repo: str, number: int) -> dict:
"""One issue with body, labels, assignees, milestone."""
return await self._get(f"/repos/{owner}/{repo}/issues/{number}")
async def get_issue_comments(
self, owner: str, repo: str, number: int
) -> list[dict]:
"""All comments of an issue (paginated)."""
return await self._get_all_pages(
f"/repos/{owner}/{repo}/issues/{number}/comments"
)
async def create_comment(
self, owner: str, repo: str, number: int, body: str
) -> dict:
"""Post a comment on an issue and return it."""
r = await self.client.post(
f"/repos/{owner}/{repo}/issues/{number}/comments",
json={"body": body},
)
r.raise_for_status()
return r.json()
async def create_issue(
self, owner: str, repo: str, title: str, body: str = ""
) -> dict: