Intent Categories Reference

Version: 3.0 (Post-M1 Floor-First) Last Updated: 2026-04-11 Coverage: 19/19 categories

Changelog


Overview

Piper Morgan’s intent classification system recognizes 19 categories (enum: services/shared_types.pyIntentCategory). After the M1 floor-first routing inversion (ADR-060, Issue #911), most query categories now route to the conversational floor — a context-assembled LLM response — rather than to canonical template handlers.

Why Floor-First

UAT Round 2 (Mar 2026) showed canned canonical templates scoring 1/3 on the Colleague Test, while floor responses (LLM + assembled context) scored 7+. The floor was already live as a fallback (#907); #911 inverted the default so that floor is the default for conversational query categories and canonical handlers run only when they offer something the LLM can’t — deterministic fast paths, database mutations, or side effects.

Routing Pipeline

User Message
  -> Pre-classifier (fast pattern match)
    -> LLM Classifier (if pre-class misses)
      -> Action Gate
         | _requires_canonical_handler(intent) -> Canonical Handler
         | _should_route_to_floor(intent)      -> Conversational Floor
         | else                                -> Workflow Dispatcher (legacy)

Source of truth: services/intent/intent_service.py, methods _requires_canonical_handler (line 9863) and _should_route_to_floor (line 9933).


The Action Gate

Two methods decide routing for categories that have been migrated to the floor-first pattern:

_requires_canonical_handler(intent) — returns True for:

Condition Rationale
PORTFOLIO (any action) Database mutations (add/delete/archive/restore)
EXECUTION (any action) External side effects (GitHub issue, todo writes)
CONVERSATION + action="greeting" Onboarding + calendar integration side effects
TEMPORAL Sub-millisecond deterministic time/date fast path
STATUS Not yet migrated; handler also triggers onboarding when no projects
PRIORITY Not yet migrated (Phase 5 of #911)
GUIDANCE + setup-topic detected Triggers the setup workflow

_should_route_to_floor(intent) — returns True for:

Categories in _FLOOR_ROUTED_CATEGORIES:

{"GUIDANCE", "IDENTITY", "DISCOVERY", "TRUST", "MEMORY",
 "CONVERSATION", "UNKNOWN"}

…unless _requires_canonical_handler overrides (e.g., a CONVERSATION greeting, or a GUIDANCE setup request).

Categories NOT in the Action Gate

ANALYSIS, SYNTHESIS, STRATEGY, PLANNING, REVIEW, LEARNING, QUERY fall through to the pre-existing workflow dispatcher path (ADR-059).


Canonical Handler Set

From services/intent_service/canonical_handlers.py::CanonicalHandler.can_handle() (line 129):

canonical_categories = {
    IntentCategory.TEMPORAL,
    IntentCategory.STATUS,
    IntentCategory.PRIORITY,
    IntentCategory.GUIDANCE,       # Setup requests only (action gate enforces)
    IntentCategory.PORTFOLIO,
    IntentCategory.CONVERSATION,   # Greeting only (action gate enforces)
}

Per Issue #963 (M1 floor inversion cleanup), IDENTITY, DISCOVERY, TRUST, and MEMORY were removed from this set. Any accidental routing to those categories now falls through to the floor rather than running dead template code.


Categories (Alphabetical)

1. ANALYSIS

2. CONVERSATION

3. DISCOVERY

4. EXECUTION

5. GUIDANCE

6. IDENTITY

7. LEARNING

8. MEMORY

9. PLANNING

10. PORTFOLIO

11. PRIORITY

12. QUERY

13. REVIEW

14. STATUS

15. STRATEGY

16. SYNTHESIS

17. TEMPORAL

18. TRUST

19. UNKNOWN


The Conversational Floor

All floor-routed intents pass through ConversationalFloor.respond() in services/intent_service/conversational_floor.py. The flow:

  1. Context assembly: ContextAssembler.gather_context() pulls category-specific structured data (calendar, projects, trust profile, capabilities, history summary, etc.)
  2. History: Last 6 ConversationTurn records — both user_message and response fields (the response field was added in #922 / commit 25437f95 so the floor sees Piper’s prior replies, not just the user’s)
  3. Floor prompt build: System prompt = base Piper identity + FLOOR_SYSTEM_PROMPT_ADDENDUM + warmth guidance. User prompt = history + [Available context: ...] block + current message.
  4. LLM call: task_type="conversation" via LLMClient.complete()
  5. Fallback: On error, _classify_llm_error picks one of FLOOR_FALLBACK_AUTH, FLOOR_FALLBACK_NO_PROVIDER, or FLOOR_FALLBACK_TRANSIENT (see llm-configuration.md).

Fabrication Guardrails (#960)

The floor system prompt (updated in commit 4789de64) explicitly prohibits inventing user data when the context block is empty. If the user asks about their todos and no todo data was assembled, the floor must say so (“I don’t see any todos in your list right now”) rather than making up plausible-looking items. This addressed the #960 class of bugs where floor responses referenced projects, issues, or meetings that did not exist.


Instrumentation



Document Status: Current as of 2026-04-11 Source files: