Skip to main content

Issue Labels โ€” Strong Suggestion

Why this exists: at one point we had 88 labels. That's worse than zero โ€” labels are only useful if they mean the same thing every time, and 88 labels does not. This document is the strong suggestion for what labels we use on humanity-org/humanwork.

Status (2026-06-03): Labels remain a strong suggestion โ€” the central taxonomy is the shared reference for humans and agents, but no CI workflow enforces it. PR authors / agents should match this set; mismatches are reviewed by humans, not stripped by bots. The label-enforcement bot was removed permanently in commit e6b93068 (it was causing too many problems on legitimate human-set labels). The LLM-classifier described further down is preserved for reference but is not wired into any active workflow.

Total label count: 34. Everything you'd want to know about an issue (kind / where / urgency / state / who-owns-it / blockers) fits in ~34 labels. If you find yourself wanting a 35th, the answer is almost always "put it in the issue title or body, not a label."

To add a new label to the strong-suggestion set, edit scripts/labels/canonical.py and open a PR โ€” don't create one-off labels in the GitHub UI. The bot is gone but the central list is still the shared reference.


TL;DR โ€” the labelsโ€‹

NamespaceCountWhat it answers
type:*3What kind of work is this? (bug / feature / epic)
area:*4Which part of the system does it touch? (backend / frontend / agent / infra)
needs:decision1Blocked on a decision
priority:p0..p34How urgent? (strong suggestion)
status:*7Where in the workflow? (strong suggestion)
role:*5Which agent owns the next move? (strong suggestion)
harness:*5Harness markers (managed / budget-exceeded / evidence-retry / security-sweep / deep-review)
GitHub defaults5good first issue, help wanted, duplicate, invalid, wontfix

A typical, well-labeled issue:

type:feature  area:backend  priority:p1  status:ready  role:eng

You almost never need more than that.


type:* โ€” kind of work (3)โ€‹

Choose exactly one of these per issue. PRs don't need a type:* (the PR title prefix carries it).

LabelUse when
type:bugSomething broken or behaving wrong in a shipped behavior
type:featureNew capability, enhancement, refactor, tech-debt, or docs โ€” anything that isn't a bug. (We don't split chore from feature โ€” the title tells the story.)
type:epicWorkstream root โ€” parent of many child tasks

Merged into here: bug, enhancement, type:chore, type:docs, type:question. Questions belong in GitHub Discussions, not as labels.


area:* โ€” what part of the system (4)โ€‹

Choose one or more. Most issues touch one area; full-stack issues touch two.

LabelScope (intentionally wide)
area:backendNestJS api/ โ€” everything server-side: controllers, services, guards, billing, channels (Slack/Email/etc.), RAG pipeline, security, database/migrations, third-party integrations, ops APIs
area:frontendNext.js frontend/ โ€” everything client-side: /client/*, /workspace/*, /ops/*, UX, accessibility
area:agentFastAPI agent/ Python service โ€” prompt config, tool calling, Specialist personas, R2 manifest sync
area:infraKubernetes, Railway, CI/CD, GitHub Actions, build tooling, test infrastructure, branch protection

Why we collapsed 16 areas into 4. Most of the legacy area:* labels (billing, channels, rag, onboarding, security, database, integrations, ops, expert-workspace, client-portal, specialists, testing) had fewer than 15 issues each, and the issue title always made the sub-area obvious ("billing: prorate Specialist rate change", "rag: ingestion retry"). One filterable dashboard with 4 buckets beats 16 narrow filters nobody remembers exist.

Where does X live?โ€‹

Sub-areaLives under
Billing / Lago / Stripearea:backend
Channels (Slack/Email/Telegram/WhatsApp)area:backend (server) + area:frontend if there's a UI
RAG / KB pipeline / data hygienearea:backend
Onboarding flowarea:backend + area:frontend
Security / RLS / autharea:backend
Database / migrations / TypeORMarea:backend
Third-party integrations (Nango, Tavus)area:backend
/ops/* (AM / SuperAdmin)area:frontend (it's a route surface)
/workspace/* (Expert)area:frontend
/client/* (Client portal)area:frontend
Specialist personasarea:agent (logic) + area:frontend (Tavus ops UI (/ops/tavus/*))
Tests / CI / e2e / perfarea:infra

needs:decision (1)โ€‹

Use this when an issue can't move forward without an architectural or product decision. One label, not three. There used to be needs:human and needs:triage; both were redundant with status:needs-human and status:ready (without role:eng), so they're gone.

LabelMeans
needs:decisionBlocked on a decision someone needs to make

priority:p0..p3 โ€” urgency (strong suggestion, 4)โ€‹

Set by the PM agent during scoping. Exactly one per issue.

LabelMeans
priority:p0Drop everything. Launch blocker / production incident.
priority:p1High โ€” needed before 10 customers, or current sprint must-have.
priority:p2Normal โ€” current quarter, default for most issues.
priority:p3Backlog โ€” nice-to-have, deferred.

Merged in: P0/P1/P2/P3, mvp-blocker (โ†’ priority:p0), high-priority/medium-priority/low-priority.

Strong suggestion โ€” see agent-team-harness/harness/labels.py. Humans set/change priority freely. The label-enforcement bot was removed 2026-06-03 (e6b93068) so non-suggested priorities are no longer auto-stripped.


status:* โ€” workflow state (strong suggestion, 7)โ€‹

Driven by the agent-team-harness FSM. Don't set by hand unless the transition is valid.

LabelMeans
status:readyScoped and ready for an agent to pick up. Also the entry state for new issues: a new issue gets status:ready + role:pm so the PM agent scopes it next tick. (The legacy status:triage was retired โ€” role:pm is enough to disambiguate "needs scoping" from "ready for eng".)
status:wipAgent actively working
status:reviewAwaiting code review
status:testAwaiting black-box testing
status:blockedBlocked on another issue/agent
status:needs-humanNeeds human intervention
status:doneCompleted

Valid transitions: ready โ†’ wip โ†’ {review, test, done, needs-human}; review โ†’ {test, ready, done, needs-human}; etc. โ€” see _VALID_TRANSITIONS in the harness source.


role:* โ€” agent ownership (strong suggestion, 5)โ€‹

Which agent role owns the next move. Set by the harness dispatcher. Don't edit by hand.

LabelMeans
role:pmPM agent โ€” scoping / triage / decomposition.
role:engEng agent โ€” implementation.
role:testTest agent โ€” black-box / e2e verification.
role:reviewReview agent โ€” code review.
role:securitySecurity agent โ€” security sweep / IDOR / auth posture (added per #1497 / #1505 / #2026).
role:contractContract-validation agent owns next action (AC testability gate before Eng).

harness:* โ€” harness markers (strong suggestion, 5)โ€‹

LabelMeans
harness:managedIssue is under harness control. Without this, the dispatcher ignores the issue.
harness:budget-exceededAgent ran out of token / wall-clock budget โ€” needs human review.
harness:evidence-retryEvidence collection step failed; harness retried (per #1497).
harness:security-sweepIssue is part of a coordinated security sweep (per #1505 / #2026).
harness:deep-reviewIssue requires deep-review treatment (per #2026) โ€” extended review budget.
harness:trivialSkip contract-validation step; route PM โ†’ Eng directly.
harness:circuit-brokenCI failing too many times โ€” needs human to fix and re-queue.
harness:debate-modeRun two implementers + judge arbitration instead of single-implementer flow.

Retired: bounce:0..3. The retry counter is now embedded as <!-- harness:bounce=N --> in the most recent agent comment. Labels are bad storage for a 0..3 integer that changes every tick.


GitHub default labels (kept, 5)โ€‹

LabelWhen
good first issueApproachable for new contributors
help wantedOpen call for outside help
duplicateAlready exists โ€” close after linking the original
invalidMisfiled or not actionable as written
wontfixAcknowledged but explicitly declined

Migration historyโ€‹

DateCutWhy
2026-05-24 (pass 1)88 โ†’ 52Folded workstream:* (duplicate of area:*), P0..P3 into priority:p0..p3, bug/enhancement/docs into type:*, mvp-blocker into priority:p0.
2026-05-24 (pass 2)52 โ†’ 30Collapsed 16 fine-grained area:* labels into 4 wide buckets. Cut type:chore/type:docs/type:question (folded into type:feature). Cut needs:human/needs:triage (duplicates of status:*). Cut harness status:triage (folded into status:ready + role:pm) and bounce:0..3 (moved to comment marker).
2026-05-25 (label-hygiene sweep)36 โ†’ 30 (repo); 95 open issues curatedDeleted 6 stale repo labels still present in the GitHub UI but not on the canonical list: P1, backend, frontend, infrastructure, bug, bounce:0. Migrated their last 4 user-issues to canonical equivalents (area:backend, area:frontend, area:infra, type:bug). Resolved 1 multi-priority conflict (#558 dropped priority:p2). Collapsed 12 epics with dual type:epic + type:feature โ†’ kept only type:epic. Added priority:p2 to 10 Workstream epics (#611โ€“#620). Added status:ready to 51 issues missing a status. Added type:feature to 38 issues missing a type. After the sweep: every open issue (95) has exactly 1 priority:*, 1 type:*, 1 status:* per the rules at the top of this doc.
2026-06-03 (label-bot removal + harness/role extension)30 โ†’ 34 (strong suggestion); enforcement removedAdded harness:evidence-retry (#1497), role:security (#1505), harness:security-sweep (#1505 / #2026), harness:deep-review (#2026). Same day, commit e6b93068 deleted the label-enforcement workflow (.github/workflows/label-enforcement.yml). Labels remain a strong suggestion; CI no longer auto-strips.

Adding a new labelโ€‹

Open a PR editing scripts/labels/canonical.py. In the PR description, justify:

  1. Which existing labels did you consider, and why don't they fit?
  2. Will this label apply to at least 20 issues in the next quarter? (Bar deliberately set high โ€” small label sets are the whole point.)
  3. Which namespace does it belong in?

If the answer to (2) is "no", you probably want a sub-task, a project-board column, or just the issue title โ€” not a new label.

Historical: prior enforcement (removed 2026-06-03)โ€‹

Before commit e6b93068, .github/workflows/label-enforcement.yml ran on issues and pull_request labeled / opened / edited events. It:

  1. Read scripts/labels/canonical.py.
  2. Removed any label not in the canonical set via the REST API.
  3. Posted a sticky comment linking back to this page.

The workflow also called scripts/labels/classify.py (OpenRouter โ€” Haiku 4.5 โ†’ gpt-4o-mini fallback) to fill in missing required slots from the just-stripped labels, confidence-gated (default โ‰ฅ0.7).

Why it was removed: the bot's auto-strip + sticky-comment loop was generating churn on legitimate human-set labels and the LLM-classifier had a low signal-to-noise ratio in practice. The taxonomy still serves as the shared reference; enforcement is now author-discipline rather than CI.

The classifier script (scripts/labels/classify.py) and scripts/labels/canonical.py are preserved in-tree for reference and ad-hoc use, but no workflow currently invokes them.