Files
2026-09-09 18:11:50 +02:00

4.1 KiB

Issue tracker: Gitea (self-hosted)

Issues and specs for this repo live as issues on the self-hosted Gitea instance at https://git.lukas-harat.com, repo lukas/Ghost_Kitchen. There's no CLI (tea isn't installed here), so all operations go through Gitea's REST API (/api/v1) via curl, authenticated with a personal access token read from the GITEA_TOKEN environment variable.

Conventions

Base URL: https://git.lukas-harat.com/api/v1/repos/lukas/Ghost_Kitchen Auth header on every request: -H "Authorization: token $GITEA_TOKEN"

  • Create an issue: POST /issues with JSON body {"title": "...", "body": "..."}. Note: Gitea's create-issue endpoint only accepts label IDs (not names) in its labels field — create the issue first, then add labels by name (below).
  • Read an issue: GET /issues/{index}. Comments separately via GET /issues/{index}/comments.
  • List issues: GET /issues?state=open&type=issues. Filter by label client-side (via jq) since query-string label filtering wants label IDs, not names.
  • Comment on an issue: POST /issues/{index}/comments with {"body": "..."}.
  • Apply labels: POST /issues/{index}/labels with {"labels": ["needs-triage"]} — this endpoint accepts label names directly, unlike issue creation.
  • Remove a label: DELETE /issues/{index}/labels/{id} — this one needs the numeric label ID; look it up via GET /labels and jq on name.
  • Close: PATCH /issues/{index} with {"state": "closed"}. Post the closing comment first (POST .../comments), then close — this call has no comment field.
  • Ensure labels exist: GET /labels to check; POST /labels with {"name": "...", "color": "#hex"} to create any of the five canonical labels that are missing before first use.

Pull requests as a triage surface

PRs as a request surface: no. (Set to yes if this repo treats external PRs as feature requests; /triage reads this flag.)

When set to yes, use the /pulls endpoints in place of /issues (GET /pulls, GET /pulls/{index}; the comment/label/close calls above are shared between issues and PRs in Gitea's API). Determine "external" by comparing the PR author against GET /repos/lukas/Ghost_Kitchen/collaborators — an author who isn't a collaborator or the repo owner is external.

Gitea numbers issues and PRs from one shared counter per repo (like GitHub), so a bare #42 may be either; resolve with GET /issues/42 and fall back to GET /pulls/42 (or vice versa).

When a skill says "publish to the issue tracker"

Create a Gitea issue via POST /issues.

When a skill says "fetch the relevant ticket"

GET /issues/{index} plus GET /issues/{index}/comments.

Wayfinding operations

Used by /wayfinder. The map is a single issue with child issues as tickets.

  • Map: a single issue labelled wayfinder:map, holding the Notes / Decisions-so-far / Fog body.
  • Child ticket: an issue carrying Part of #<map> at the top of its body and label wayfinder:<type> (research/prototype/grilling/task) — this Gitea version (1.27.2) has no native sub-issue link, so the task-list/text-reference fallback is the only option here. Once claimed, assign the driving dev (PATCH /issues/{index} with {"assignees": ["<username>"]}).
  • Blocking: Gitea's native issue dependencies — the canonical representation (confirmed available on this instance via its swagger spec). Add one with POST /issues/{child}/dependencies body {"index": <blocker-index>, "owner": "lukas", "repo": "Ghost_Kitchen"} — this makes the child depend on (i.e. be blocked by) the blocker. List with GET /issues/{index}/dependencies. A ticket is unblocked when every listed dependency is closed.
  • Frontier query: list the map's open children, drop any with an open (not-closed) entry in GET /issues/{index}/dependencies or an assignee; first in map order wins.
  • Claim: PATCH /issues/{index} with {"assignees": ["<username>"]}, the session's first write.
  • Resolve: POST /issues/{index}/comments with the answer, then PATCH /issues/{index} with {"state": "closed"}, then append a context pointer (gist + link) to the map's Decisions-so-far.