Issue tracker: Gitea
Last updated
May be stale
This page was last updated ago, on 7 October 2026. Check what it says against the code before relying on it.
Issues, specs and pull requests for this repo live on Gitea at
limelight-development/cityrp2.
Use the tea CLI; AGENTS.md covers install and login. Inside a
clone it infers the login and repo from git remote -v.
Don't use gh: GitHub is no longer the source of truth. Its issues were migrated here with new
numbers, then backfilled with the org's labels:
- Each migrated body opens with who created it on GitHub and an
Original GitHub issue:URL. - A
#Nin commit messages, docs,AGENTS.mdor branch history is still a GitHub number, because git history wasn't rewritten. Find its Gitea issue through theOriginal GitHub issue:line of the migrated body, never by the number alone. - GitHub's issue types and Priority/Effort fields became
type/,priority/andeffort/labels, and its sub-issue links became thePart of #n/## Ticketsbody convention.
Gitea has no issue types, issue fields, sub-issues, close reasons or project-board API. This
repo records all of them as org-level scoped labels, alongside Gitea's native issue dependencies.
The gitea-issues skill is the authority on the
mechanics: the API calls, the query quirk below, the repo labels, and the parent/child convention.
Read it before any filtered query or bulk edit. Getting this wrong is footgun #20 in
AGENTS.md.
The GitHub CityRP2 Project Board has no counterpart here. Triage state is a status/ label, and
who is working on an issue is its assignee.
Labels
Every label below is defined once on the limelight-development org, and every scope is
exclusive: adding a second label from a scope replaces the first.
| Scope | Values | Stands in for |
|---|---|---|
type/ |
bug, task, suggestion, research, discussion, stub, spec, initiative, epic, wayfinder, feedback, pull-request |
Issue type |
priority/ |
urgent, high, medium, low, none |
Priority field |
effort/ |
high, medium, low |
Effort field |
status/ |
new, awaiting-review, needs-info, triaged |
Triage state, see triage-labels.md |
state/ |
blocked |
Stopped by hand on something that isn't an issue, see Dependencies |
resolution/ |
not-planned, duplicate, cannot-reproduce |
Close reason |
priority/none means triaged and deliberately parked. It is not a placeholder for "not decided
yet".
| Type | Priority | Effort |
|---|---|---|
bug, task, research |
required | required |
suggestion, stub, spec, initiative, epic, wayfinder |
required | none |
discussion, feedback, pull-request |
none | none |
Types fall into three groups; the skill's Choosing a type section has the criteria for each.
- Inbound:
suggestion(approved on the website) andbug(from the website, staff, developers or Sentry). They arrive unsized. Triage sizes a bug; a suggestion is sized once/to-ticketsbreaks its spec down. - Planned:
stub, a minimal idea that/grill-to-specgrills and rewrites in place as aspec;spec, which/to-ticketsturns into a singletaskwhen it is small enough or anepic; then the laddertask<epic<initiative. A task is one piece of work, an epic is one feature, and an initiative groups several features or fixes under one goal, holding epics, tasks or both. A stub or a spec can sit under an initiative or an epic while it waits. - Decision and legacy:
research,discussion,wayfinder,feedback,pull-request.
type/epic is what GitHub called Scheme of Work. No skill creates a type/initiative or a
type/stub; both are set by hand. type/pull-request marks a GitHub pull request migrated as an issue; no cityrp2 issue
carries it, and new pull requests are real Gitea PRs.
The repo's own labels are topic labels (documentation, database, ci/cd, …) that go alongside
a type/ label. The GitHub-era bug, enhancement, feature, suggestion, duplicate and
no-repo labels are superseded by the scopes above. The skill's
Repo labels section has the full list.
Types the skills produce
Criteria for every type are in
the gitea-issues skill. These are
the ones the engineering skills produce:
| Skill concept | Type label |
|---|---|
A report filed by /file-issue |
type/bug, type/task, type/discussion or type/research, always status/new |
A sidebar filed by the issue-reporter agent |
The same four at status/new, or type/stub at status/awaiting-review |
| A bug confirmed during triage | type/bug |
A spec from /to-spec or /grill-to-spec, not yet broken down |
type/spec, always status/awaiting-review |
A type/stub after /grill-to-spec |
The same issue, re-typed type/spec at status/awaiting-review |
Internal dev work, a ticket from /to-tickets, or a spec /to-tickets sized as one ticket |
type/task |
A body of work spanning several tasks: the parent /to-tickets splits |
type/epic |
A /wayfinder map |
type/wayfinder |
A /wayfinder grilling ticket (a decision to be reached) |
type/discussion |
A /wayfinder research ticket |
type/research |
A /wayfinder prototype or task ticket |
type/task |
type/suggestion comes from the website's feedback sync, and
type/feedback is reserved for the PR back channel. Neither is for these skills to assign.
Conventions
-
Start work on an issue:
tea issues edit <n> --add-assignees <you>before the first edit.tea whoamiprints your username. An open, unassigned issue reads as unclaimed, and a second agent picks up the same work. -
Create an issue: set every label in the create call, always including a
type/label:tea issues create --title "..." --description "$(cat body.md)" \ --labels "type/task,priority/medium,effort/low,status/triaged" -
Read an issue:
tea issues <n>for the body and labels,tea comments list <n>for the thread. -
Comment:
tea comment <n> -- "..." -
Change a label:
tea issues edit <n> --add-labels status/triaged. Within a scope, adding is enough: the old value drops off. -
List or filter: see Querying.
-
Close: see Closing an issue. Every closure gets a reason.
Querying
tea issues list --labels silently ignores org labels and returns every issue. The labels
parameter on GET /repos/{owner}/{repo}/issues does the same. Use the org-wide search endpoint
instead, which does resolve them:
tea api "/repos/issues/search?owner=limelight-development&type=issues&state=open&labels=status/new&limit=50" \
| jq '[.[] | select(.repository.full_name == "limelight-development/cityrp2") | {number, title}]'
It spans every repo in the org, it ORs several labels rather than ANDing them, and pages hold at
most 50. The skill has a paging helper (q), and an all_open helper for "has no label from this
scope" queries, which search can't express.
Closing an issue
Gitea's close carries no reason, so the reason is a label applied before closing:
| Situation | Do |
|---|---|
| Resolved, or found to be already implemented | tea issues close <n>, with no resolution/ label |
| Out of scope, rejected, won't be actioned | tea issues edit <n> --add-labels resolution/not-planned, then close |
| Already tracked by another issue | resolution/duplicate, then close |
| A bug that can't be reproduced | resolution/cannot-reproduce, then close |
A closed issue with no resolution/ label counts as completed. Always leave a comment saying why,
naming the other issue for a duplicate.
The website's feedback sync reads these labels on the issues it filed: closed with any
resolution/ label shows the player a denial, and closed without shows completed. Apply the label
before closing. A label added or removed after the close still corrects the status, but it
notifies the post's author a second time.
A not-planned closure of a feature request also creates or updates a file in .out-of-scope/,
which /triage reads on future issues to catch re-litigation.
Dependencies
Blocked-by uses Gitea's native issue dependencies, which are enabled on this repo and shown in the
issue sidebar. tea has no flag for them, so the calls are in
the skill. A ticket is unblocked when
every issue it depends on is closed and it doesn't carry state/blocked.
state/blocked is for a blocker a dependency can't name: an artist delivering content, someone
coming back from leave, a decision being taken off the tracker. Whoever adds it also adds a line to
the body saying what the issue waits on, so the next reader knows when to take it off:
Blocked on: the sprocket model from the art team.
Remove the label and the line together once that thing arrives. It isn't a substitute for a dependency: if the blocker is an issue in any org repo, link it natively instead. On an epic or initiative, it holds back every child under it as well.
Pull requests
Open one with tea pulls create --base master --title "..." --description "$(cat pr-body.md)".
--draft prefixes the title with WIP: , which Gitea treats as a draft. Branch naming, the
public= placement, what the description carries (the in-game test plan and the test-suite
changes) and the lint gates are in artifacts.md and
AGENTS.md.
Pull requests carry no triage labels. /triage given a PR runs a sanity check on it instead:
up to date, still needed, still in scope. See
the triage skill.
When a skill says "publish to the issue tracker"
Create a Gitea issue with:
- a
type/label priority/andeffort/as the type needs themstatus/triagedif the skill says the issue is born triaged (/to-tickets),status/awaiting-reviewfor a spec from/to-specor/grill-to-spec, orstatus/newotherwise
All in one tea issues create.
When a skill says "fetch the relevant ticket"
tea issues <n>, plus tea comments list <n> when the thread matters.
Wayfinding operations
Used by /wayfinder. Gitea has no sub-issues, so the map lists its own children, following the
skill's parent and child convention.
-
Map: a
type/wayfinderissue with priority set and no effort. Its body holds the Destination, Notes, Decisions-so-far, Not-yet-specified and Out-of-scope sections, plus a## Ticketschecklist. That checklist is the only list of the map's children. It stands in for the query/wayfinderwould run against native sub-issues. -
Child ticket: its body opens with
Part of #<map>., and it is added to the map's## Ticketschecklist in map order. Its wayfinder type is itstype/label:type/researchfor researchtype/discussionfor grillingtype/prototypefor prototypetype/taskfor task
Set priority, and effort where it is known. Once claimed, the ticket is assigned to the driving dev.
-
Blocking: native dependencies (see Dependencies).
-
Frontier query: read the map's
## Ticketschecklist in order. For each unticked child, read its state and assignees, and itsdependencieslist for any open blocker. The frontier is the open, unassigned children with no open blocker. The first one in checklist order wins. -
Claim: assign yourself. It is the session's first write.
-
Resolve:
- Comment the answer.
tea issues close <n>.- Tick the child in
## Tickets. - Append a context pointer to the map's Decisions-so-far.
-
Rule out of scope:
- Add
resolution/not-plannedand close. - Remove the child from
## Tickets. - Add one line to the map's Out of scope section.
It stays out of Decisions-so-far, which records the route actually walked.
- Add
Publishing a spec
A finished spec becomes one or more typed issues rather than staying in the conversation, and anything raised in passing gets its own issue too. The rules are in artifacts.md.