HOW TO MAINTAIN — Spec Kitty Issue Tracker

Maintainer entry-point for tracker hygiene. Authored 2026-06-16 (planner-priti, governance op). Grounded in live repo data (spec-kitty/spec-kitty) and the operator conventions in work/TRACKER_DOCTRINE_NOTES.md. Items marked (inferred) are derived from observed usage, not an explicit written rule; everything else is confirmed from the label taxonomy, native GitHub Types, or an existing convention doc.

Decision rationale for the milestone / release-goal recommendations lives in §5 Recommended additions (per Directive 003 — Decision Documentation).


1. Issue tracker structure

Three structural roles. Keep them distinct — conflating them is the most common drift.

Role What it is Parenting rule
Functional epic A domain that produces code/behaviour — a subsystem, capability, or bug-class. Labelled epic, native Type Feature. Owns work. Functional tickets are parented here via native sub-issues.
Functional ticket A single Bug / Task / enhancement that changes the product. Lives under exactly one functional epic (single-parent constraint).
Meta-tracker A release / go-no-go / stabilization rollup (e.g. 3.2.0 release tracker). Not a domain of work. NEVER the canonical parent of functional tickets. Prefix META-TRACKER:. References work via a body checklist only.

The cardinal rule (confirmed — CLAUDE.md "Meta vs functional epics" + TRACKER_DOCTRINE_NOTES §1):

Functional epics own work; meta-trackers only reference it. Never make a meta-tracker the parent of a functional ticket — use a checklist in the meta-tracker body, and parent the ticket under its functional epic.

Live example of the pattern done right: #1929 (Tracking: post-#1908 adversarial-panel findings) is a checklist-only meta view; each of its four findings (#1915–#1918) is canonically parented under its functional epic (#1795/#1868/#1666/#1914), and #1929 explicitly states it is not their parent.

Native sub-issues are the source of truth

  • Parent/child is the native GitHub sub-issue graph, not body checklists (checklists are invisible to tooling). Confirmed: 29 of 35 open epics already use native sub-issues; e.g. #1619 carries 10 native children. Backfill native links wherever a body checklist implies a parent.
  • REST: POST/DELETE /repos/{owner}/{repo}/issues/{n}/sub_issues; sub_issue_id is the integer database id (use gh api -F, not -f). Single-parent → DELETE from the old parent before POST.
  • .parent is reliable only via GraphQL (issue.parent), not REST.

Hygiene invariants (every open ticket)

  1. Resolves to a functional epic — not an orphan, not meta-rooted, not under a closed/superseded epic.
  2. Has an issue Type (Task / Bug / Feature; epics = Feature). See §3.
  3. Has a priority label (priority:P0..P3). See §2.

Sweep closed epics for open children and rehome them — open tickets under a closed parent look tracked but are invisible.

How a mission maps to issues

A mission's issue-matrix (in the mission spec) lists every tracker issue the mission addresses. At spec time:

  • Every addressed issue → a row in the issue-matrix and a tracker comment naming the mission.
  • The issue is claimed (assigned to the operator) per the claim discipline below.

Claim discipline (operator rule, 2026-06-16):

  • Claim before WORKING a ticket — assign it to yourself/operator before starting implementation, so concurrent agents/contributors don't collide.
  • Closing / cleanup is exempt. You may close a provably-done ticket without claiming it first — attach an evidence comment (the PR/commit/test proving it's done). No claim needed to close.

2. Priority levels

Priority is a tracker-state label (priority:P0..P3), distinct from the MoSCoW planning lens (MoSCoW = a scoping discipline at mission-negotiation time; priority:Px = backlog state). Both coexist; do not collapse one into the other.

Operational meaning is confirmed from the label descriptions and (inferred) from observed application across the current 3.2.x stabilization lane:

Label Label description (confirmed) Operational meaning (inferred from usage) Open count Example
priority:P0 Release blocker / must decide before final 3.2.0 Release/merge blocker. Must be resolved or explicitly decided before the targeted release ships. 7 #1844 (rc verify blocked), #1619 (exec-context epic)
priority:P1 High-value stabilization / bug or release confidence Important — high-value stabilization or release-confidence work; not a hard ship-gate but strongly targeted for the cycle. 63 #1978 (naming split-brain), #1945 (ToolSurfaceContract epic)
priority:P2 Planned enhancement / post-blocker work Normal — planned enhancements / cleanup scheduled after blockers clear. 83 #1979, #1928 (lint/strict debt epic)
priority:P3 Backlog / future / needs reconfirmation Low — backlog; future or needs reconfirmation before it's actionable. 55 #1973 (experiment), #1911

Triage rule: assign a provisional priority and flag it rather than silently guessing. The p1-decision:* and triage:* labels (see §3) record in-flight triage decisions on top of the base level.


3. Issue Types

Confirmed: the repo uses GitHub's native org-level issue Types (not type-via-label). Three types are enabled: Task, Bug, Feature (GitHub's built-in default set — note this Feature is GitHub's generic Type and is unrelated to the prohibited domain term "feature/Mission"; do not rename it).

Type Use for
Bug An unexpected problem or incorrect behaviour to fix.
Task A specific, scoped piece of work (refactor, chore, doc, tech-debt item).
Feature A request / new capability and all epics (epic = Feature + epic label).

Set Type via GraphQL updateIssue(input:{id, issueTypeId}) (type ids are per-repo). Derive Type objectively — from the conventional-commit prefix + labels — not by guesswork.

Adoption gap (inferred — action item): Type coverage is incomplete. In an 80-issue recent-open sample, ~24% (19/80) had no Type set (e.g. #1929, #1978 at time of audit). Backfilling Type on untyped open issues is a standing hygiene task.


4. Labels

The label taxonomy is documented in one place: Managing the issue tracker → Label taxonomy. It is the canonical authority for every label family (priority, classification, domain:*, concern, triage:*, provenance from:*, PR-workflow, lifecycle, legacy).

Labels never encode kind: the bug and enhancement labels and the type:* label family are retired and were deleted from the repository on 2026-10-19; the native Type above is the sole kind carrier. There is no future label (paused work is deferred), and release scope lives in milestones (§5), not labels. The labels this section previously listed (research-mission, doctrine, agent_profiles, oauth-ddd-refactor, launch-blocker, mvp, the 3.2.0 / 3.3.0 version labels and p1-decision:*) no longer exist.


5. Release scoping: emergent milestones + declarations of intent (IMPLEMENTED)

Adopted 2026-06-16. Milestones were previously abandoned (2 historical, no due-dates, zero open issues; release scoping rode on version labels 3.2.0/3.3.0). The model below is now live.

5a. Emergent milestones (per minor cycle, not per patch)

Scope by minor cycle: milestones are 3.2.x, 3.3.x (+ a retroactive point-in-time 3.2.0 for what shipped). A minor cycle has one goal; multiple emergent patches (3.2.1, 3.2.2, …) each advance that same goal, and the milestone stays open until the goal is structurally met. This lets us split a minor cycle into as many patches as the work needs without re-litigating scope.

  • Current milestones: 3.2.0 (#3, closed — 7 shipped issues) · 3.2.x (#4, open — the active stabilization cycle) · 3.3.x (#5, open — next cycle, #1878 write-side + forward).
  • What it buys: a real "what's left this cycle" burndown for free; one stable target across patches.
  • Gotcha (record this): gh issue edit --milestone "<title>" only resolves open milestones by title. To attach issues to a closed (retroactive) milestone, use the API by number:
    unset GITHUB_TOKEN
    gh api repos/spec-kitty/spec-kitty/milestones -X POST -f title="3.2.x" -f state="open" -f description="<one-line goal + link to docs/changelog/3.2.x.md>"
    gh issue edit <num> --repo spec-kitty/spec-kitty --milestone "3.2.x"        # open milestone, by title
    gh api repos/spec-kitty/spec-kitty/issues/<num> -X PATCH -F milestone=<N>   # any/closed milestone, by number
    

5b. Declarations of intent (the canonical goal home)

The release goal lives in a version-controlled doc, not (only) the milestone description: docs/changelog/<minor>.md — the durable, PR-reviewed declaration of intent (goal · rationale · scope · non-goals · success criteria · emergent-patch plan · links). The milestone description is a short pointer back to it (it is ephemeral, char-limited, unreviewable, and can't link to research).

  • Why a doc over the milestone description: a "declaration of intent" deserves git history + review and must link to the research/missions; the milestone is the burndown surface, the doc is the intent.
  • Connection to mission practice: a cycle goal maps to a driving mission (or small named set); the mission's issue-matrix.md is the per-patch closure ledger that rolls up into the milestone burndown.
  • See docs/changelog/release-goals.md for the convention and docs/changelog/3.2.x.md for the live example (3.2.x — "Strangle the naming/identity/read-path split-brain toward a consistent SSOT.").

Quick maintainer checklist

  • [ ] Every open issue: functional-epic parented (native sub-issue), Typed, prioritized.
  • [ ] No functional ticket parented under a META-TRACKER: issue.
  • [ ] Backfill native sub-issue links where a body checklist implies a parent.
  • [ ] Backfill issue Type on the ~24% currently untyped.
  • [ ] Claim before working; close-with-evidence is claim-exempt.
  • [ ] (Proposal) Stand up a v3.2.1 milestone + one-line release goal in its description.