## Goal

Choose the smallest topic set that would route this item to the right
maintainer interest bucket without hiding a central second concern.

This is a fuzzy multi-label task. Avoid both failure modes:

- Over-labeling: adding topics for implementation details, tests, examples, or
  side effects.
- Under-labeling: dropping a topic that is explicitly central to the title or
  first clear problem statement.

## Decision Process

Do these checks internally, then call `final_json`.

1. Read the title first. Identify the main user-visible problem or feature.
2. Pick the single best primary topic.
3. Scan the title and first clear body summary for explicit central facets:
   protocol/runtime, provider/model serving, session/state, execution/tooling,
   channel/UI, memory, security/auth, config, reliability, docs/tests.
4. Add a secondary topic only when removing it would misroute a maintainer who
   follows that bucket.
5. Delete topics that are only file locations, validation work, examples,
   dependencies, likely consequences, or broad words from comments.
6. Do a final cardinality check:
   - 0 topics when no allowed topic is central.
   - 1 topic for a single-focus item.
   - 2 topics for normal cross-topic items.
   - 3 topics when the title/body explicitly has three central facets.
   - 4+ topics only for explicit multi-system coordination, not broad impact.

Do not optimize for one label. Optimize for the smallest complete set.

## Runtime Discipline

This is a routing task, not a full PR review.

- Do not audit every allowed topic.
- Do not summarize the whole issue, comments, or diff.
- Do not inspect repository files when the title and GitHub context are enough.
- Stop once the primary topic and any explicit central secondary facets are
  identified.
- Call `final_json` immediately after the final cardinality check.

## Evidence Priority

- Title is strongest.
- The first clear problem statement or summary in the body is next.
- Labels, changed files, comments, and diff can confirm or disambiguate, but
  they must not broaden the topic set by themselves.
- Tests and docs files are weak evidence unless tests or docs are the subject.
- If context is truncated, classify from the visible central evidence and add a
  caveat only when that materially limits confidence.

## Central Facet Recovery

Before final output, check whether any of these central facets were dropped:

- A named protocol or protocol adapter can be central: ACP, ACPX, MCP, gateway
  protocol, OpenAI-compatible streaming, Responses API, chat completions.
- Agent runner, backend, subagent, harness, heartbeat, lifecycle, or orchestration
  behavior can be central as `agent_runtime` or `coding_agents`.
- Session identity, binding, persistence, transcript, resume, reset, or
  parent/child state can be central as `sessions`.
- Execution control, shell commands, tool allow/deny, durable exec, or tool
  invocation can be central as `exec_tools`, `approvals`, `sandboxing`, or
  `tool_calling` depending on the exact behavior.
- Local/self-hosted model backends, local hardware, OpenAI-compatible local
  serving, embeddings providers, and model endpoint behavior can be central as
  `local_models`, `local_model_providers`, `model_serving`, or
  `self_hosted_inference`.
- Named chat surfaces, browser/web UI, memory, security/auth, config, queueing,
  notifications, and telemetry can be central when the title or summary makes
  them the subject.

If a facet is central but maps to a narrower allowed topic, use the narrower
topic instead of a broad fallback.

## Enum Discipline

- Output only exact allowed topic ids.
- Never invent shorthand such as `cli`, `tts`, `openrouter`, `status`,
  `thread`, `provider`, `tool`, or `test`.
- If the closest word in the title is not allowed, map it to the nearest
  allowed topic or omit it.

## Positive Cues

- Counts, usage, cost, tokens, metrics, diagnostics, traces, and status
  reporting route to `telemetry_usage`.
- Subagents, coding-agent runs, harness behavior, approvals, sandboxing,
  compaction, or agent orchestration route to `coding_agents`.
- Agent runner, embedded backend, lifecycle, heartbeat, attempt, orchestration,
  or runtime state route to `agent_runtime` when the agent runtime itself is
  central.
- LM Studio, Ollama, llama.cpp, GGUF, local hardware, local model compatibility,
  local fallback, and local context behavior route to `local_models`.
- OpenAI-compatible serving, base URL normalization for model endpoints,
  streaming, usage chunks, vLLM/TGI/LocalAI serving, endpoint lifecycle, and
  request routing route to `model_serving`.
- Self-hosted embeddings, memory providers, speech services, private inference,
  and locally operated inference services route to `self_hosted_inference`.
- Named Discord, Telegram, Slack, Zulip, Feishu, webchat, or similar surfaces
  route to `chat_integrations`; generic notify policy/delivery gates route to
  `notifications`.
- Chat UI display/status/footer behavior routes to `ui_tui` only when the
  user-facing interface is central.
- Test-only contract follow-through routes to `tests_ci` only when the testing
  surface itself is the subject; otherwise route by the runtime contract being
  tested.

## Hard Local Model Filters

Before final output:

- Delete `local_model_providers` unless the item explicitly centers a local,
  self-hosted, or user-declared OpenAI-compatible backend: LM Studio, Ollama,
  vLLM, TGI, LocalAI, llama.cpp, Atomic Chat, localhost/LAN, or private
  inference.
- Never use `local_model_providers` for hosted provider catalogs/manifests,
  hosted model availability, hosted auth, usage/billing UI, TTS/speech/image
  behavior, or hosted APIs. Hosted catalog updates are `model_releases` and
  sometimes `config`; hosted usage/billing/status work is `telemetry_usage` or
  `ui_tui`.
- Use `local_models` only for concrete local/offline model execution or
  compatibility; not model IDs, catalogs, lists, static entries, provider
  manifests, or hosted provider availability. Keep it for local-model lean
  filtering and local-model runtime crashes.

## Over-Label Guardrails

- `api_surface`: external API, CLI, or HTTP contracts only. Not internal
  payloads/options/functions, status text, UI events, or ordinary command
  behavior.
- `reliability`: operational failures such as timeout, crash, leak, retry,
  stuck state, data loss, cleanup, or recovery. Not a generic bug tag.
- `sessions`: session lifecycle/state/storage/identity only. Not every item
  mentioning session context or files.
- `local_model_providers`: provider setup/routing/auth/discovery/compatibility
  only. Not every local endpoint issue.
- `config`: configuration behavior itself, not any feature with an option.
- `docs` and `tests_ci`: only when docs or test tooling is the subject.
- `security`: security boundary, credentials, auth hardening, SSRF, token
  handling, or vulnerability only. Not every risky-looking behavior.
- `gateway`: gateway daemon, gateway API, startup, restart, or gateway-owned
  routing only. Not every item that passes through the gateway.

## Tie-Breakers

- Count/usage/token/cost/metric/trace/diagnostic/status/footer-count features
  are `telemetry_usage`, even if shown in UI or session status.
- Base URL normalization, endpoint lifecycle/selection, streaming, request
  routing, OpenAI-compatible serving, vLLM/TGI/LocalAI behavior, and model
  endpoint compatibility are `model_serving`.
- TTS, shell/exec, command, tool invocation, allowlist, and execution-control
  behavior are `exec_tools` when the feature controls execution or spoken/tool
  output.
- Thread/session isolation, per-session binding, fallback recovery state, and
  lifecycle state are `sessions` when those boundaries are central.
- Structured tool result display, stdout rendering for tool results, pre-tool
  text preservation, and tool-call transcript/content handling are
  `tool_calling` when tool-call semantics are central.
- Delivery fallback, outbound recovery, lost final/pre-tool text, duplicate
  cleanup, and lifecycle recovery are `reliability` when recovery correctness is
  central.

## False-Positive Suppression

- Do not use `local_model_providers` for base URL normalization,
  OpenRouter/OpenAI-compatible endpoint fixes, endpoint lifecycle, streaming,
  usage chunks, or vLLM/TGI/LocalAI serving. Use `model_serving` unless provider
  setup/auth/discovery/routing is central.
- Do not use `notifications` for named Discord/Telegram/Slack/Zulip/Feishu
  behavior, ACP final/pre-tool text preservation, delivery fallback recovery, or
  outbound recovery correctness. Use `chat_integrations` for named chat surfaces
  and `reliability` for recovery/loss/fallback.
- Do not use `tool_calling` for TTS tags/options, browser screenshot/vision,
  generic tool output, or config-like options.
- Do not use `api_surface` for parse helpers, CLI edge-case tests, token
  parsing, status/footer display, internal command behavior, or local model
  compatibility.
- Do not use `config` merely because a feature adds an option. Route by what
  the option controls.

