CityRP Documentation Repository

Issue tracker: Gitea

Last updated

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 #N in commit messages, docs, AGENTS.md or branch history is still a GitHub number, because git history wasn't rewritten. Find its Gitea issue through the Original GitHub issue: line of the migrated body, never by the number alone.
  • GitHub's issue types and Priority/Effort fields became type/, priority/ and effort/ labels, and its sub-issue links became the Part of #n / ## Tickets body 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) and bug (from the website, staff, developers or Sentry). They arrive unsized. Triage sizes a bug; a suggestion is sized once /to-tickets breaks its spec down.
  • Planned: stub, a minimal idea that /grill-to-spec grills and rewrites in place as a spec; spec, which /to-tickets turns into a single task when it is small enough or an epic; then the ladder task < 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 whoami prints 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/ and effort/ as the type needs them
  • status/triaged if the skill says the issue is born triaged (/to-tickets), status/awaiting-review for a spec from /to-spec or /grill-to-spec, or status/new otherwise

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/wayfinder issue 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 ## Tickets checklist. That checklist is the only list of the map's children. It stands in for the query /wayfinder would run against native sub-issues.

  • Child ticket: its body opens with Part of #<map>., and it is added to the map's ## Tickets checklist in map order. Its wayfinder type is its type/ label:

    • type/research for research
    • type/discussion for grilling
    • type/prototype for prototype
    • type/task for 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 ## Tickets checklist in order. For each unticked child, read its state and assignees, and its dependencies list 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:

    1. Comment the answer.
    2. tea issues close <n>.
    3. Tick the child in ## Tickets.
    4. Append a context pointer to the map's Decisions-so-far.
  • Rule out of scope:

    1. Add resolution/not-planned and close.
    2. Remove the child from ## Tickets.
    3. Add one line to the map's Out of scope section.

    It stays out of Decisions-so-far, which records the route actually walked.

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.