Status: Approved Date: 2026-03-19 Supersedes: ADR-039 routing philosophy (ADR-039 infrastructure retained) Issues: #911 (floor inversion), #922 (conversation continuity bug) Deciders: PM, Chief Architect, PPM, CXO, CIO (roundtable consensus, 2026-03-14)
Piper Morgan’s intent routing architecture (established in ADR-039, October 2025) used a dual-path system: canonical handlers for fast, deterministic responses (~1ms) and workflow handlers for complex, LLM-powered operations (2-3s). When a user’s message didn’t match any handler, the system returned a canned deflection: “I don’t have that capability yet.”
This architecture produced a paradoxical outcome: Piper Morgan — a sophisticated PM assistant with 19 intent categories, 62 patterns, entity models, trust gradients, and guided workflows — was worse than a generic ChatGPT wrapper for any request that fell outside its pre-built handlers.
A generic wrapper with a one-line system prompt (“You are a helpful PM assistant”) would engage conversationally with any PM question. Piper refused to try.
During manual QA testing (March 2026), a user asked: “Can you help me manage the agents working on a coding assignment for me?” — a reasonable PM request. Piper responded: “I don’t have that capability yet! Try asking ‘What can you do?’”
Further testing revealed that most messages hitting canonical handlers received template boilerplate regardless of what the user actually asked. The handlers were designed for speed and determinism, not for conversational quality. The LLM — the thing that makes conversational AI conversational — was used to classify messages but never to respond to them.
The ADR-039 architecture treated structured handlers as the entire capability surface. Anything outside handler boundaries was a dead end. The LLM was behind a classification gate, not available as a conversational floor.
ADR-039 Architecture:
Message → Classifier → [match?] → Handler (fast or workflow)
→ [no match?] → "I can't do that" (DEAD END)
Piper is always at least as good as a well-prompted LLM with the user’s context. Structured handlers make it better than that, not different from that.
The LLM conversational floor is the default response path. Structured handlers are enhancements that provide better experiences for specific capabilities (side effects, integrations, guided workflows). The user never hits a dead end.
Message → ProcessRegistry (guided process check)
→ Classifier → Action Gate
├── Side effect required? → Canonical/Workflow Handler
├── High-confidence deterministic? → Fast-path canonical (narrow)
└── Everything else → Context Assembler → Conversational Floor
Action Gate criterion: “Does this intent require an operation the LLM cannot perform within the floor response?”
Three cases route to handlers:
Everything else — including GUIDANCE, DISCOVERY, TRUST, MEMORY, STATUS (read-only), PRIORITY, TEMPORAL (calendar), CONVERSATION (non-onboarding), UNKNOWN, and unhandled actions — routes to the conversational floor with assembled context.
Each intent category gets a gather_context() function that returns structured data for injection into the floor prompt:
Design principles for the assembler:
The floor responds as a PM colleague — engaging directly with what the user asked, using their project context, offering concrete actions Piper can take. It never says “I can’t do that.” It never apologizes for missing features. It just helps.
When an action would require a capability Piper doesn’t have, it suggests an alternative action it can take — naturally, without highlighting the limitation.
ADR-039 is not revoked. The infrastructure it established — pre-classifier, LLM classifier, canonical handler framework, workflow factory — remains in place and in use. What changes is the routing philosophy:
| Aspect | ADR-039 (Oct 2025) | ADR-060 (Mar 2026) |
|---|---|---|
| Default path | Canonical handler | Conversational floor |
| LLM role | Classification only | Classification + response generation |
| Unmatched queries | Deflection | Floor with context |
| Handler purpose | Primary response mechanism | Enhancement for side effects |
| Floor | Last resort (UNKNOWN only) | Default for all read-only queries |
ADR-039’s fast-path concept survives as the narrow deterministic exception in the Action Gate. ADR-039’s workflow path survives for EXECUTION and other side-effect categories. The dual-path architecture becomes a three-path architecture: fast-path (narrow), action-path (side effects), floor-path (default).
| Risk | Mitigation |
|---|---|
| Latency regression for simple queries | Narrow fast-path exception for truly deterministic responses |
| LLM cost at scale | Cache context assembly, single model for now with split-ready abstraction |
| Floor quality inconsistency | Sample-based human review at alpha, Colleague Test rubric |
| Context assembler misses data | Incremental migration, fail-graceful design |
| Action triggers lost in floor routing | Action Gate checks before routing, not after |
| Floor prompt parroting | Directive prompt style (“respond directly”) not descriptive (“I’m here to help”) |
_GENERIC_CANONICAL_SIGNATURES (confirms inversion complete)Decision: All IDENTITY queries route to floor. The previous “core IDENTITY canonical / adjacent IDENTITY floor” distinction is retired.
Rationale: M1 Gate UAT Round 2 (Apr 7) showed the canned “I’m Piper Morgan…” identity template scoring 1/3 on the Colleague Test. Direct floor testing of the same query scored 7+. The case for keeping core IDENTITY canonical (consistency, fast-path latency) was outweighed by the quality gap.
Code change: services/intent/intent_service.py _requires_canonical_handler() now returns False for category "IDENTITY" unconditionally. The _is_adjacent_identity() method is dead and the corresponding _handle_identity_* methods in canonical_handlers.py are unreachable in production.
Commit: 33e6758a (PM-approved Apr 8, verified in Gate UAT Round 4 Apr 8 with 7/9 PASS).
Decision: Add response field to ConversationTurn and backfill it after each successful processing.
Rationale: The floor was reading conversation history from conv_context.turns, but the in-memory ConversationTurn had no response field. The floor was getting only the user’s previous messages, never Piper’s replies — losing the half of the conversation needed for continuity. This was the underlying cause of the “OK” affirmation failure mode (#922).
Code change:
services/intent_service/conversation_context.py — added response: Optional[str] = None field to ConversationTurnservices/intent/intent_service.py — process_intent() now writes result.message to the latest turn’s response field after successful processingStatus: Partial fix. The data plumbing is correct but the floor LLM still struggles with single-word inputs in some edge cases. Tracked as #922 carried into M2.
Commit: 25437f95.
Decision: Add a hard prohibition in the floor system prompt against inventing user data when the context block is empty or missing that data.
Rationale: M1 Gate UAT Round 5 (Apr 11) discovered that when “list todos” fell to the floor without canonical handler context (because of a pre-classifier pattern miss), the LLM hallucinated nine fancy fabricated todos with PM-style descriptions. None existed in the database. This is worse than “I don’t have that data” because it looks authoritative.
Code change: services/intent_service/conversational_floor.py system prompt addendum now includes:
Companion fix: Pre-classifier pattern updated to match “list todos” / “show todos” without requiring “my” — closes the immediate route. Commit: 063edf52.
Status: Immediate guardrail in place. Deeper architectural fix (context contract enforcement) tracked as #960 and #961 (route audit) for M2.
Commit: 4789de64.
dev/2026/03/15/floor-inversion-architecture-report.mdmemo-ppm-floor-inversion-synthesis-2026-03-16.mdmemo-arch-floor-inversion-review-2026-03-16.md52e6cfcc| Date | Reviewer | Decision |
|---|---|---|
| 2026-03-14 | PM, Architect, PPM, CXO, CIO | Unanimous roundtable consensus on principle and immediate action |
| 2026-03-16 | Architect, PPM, CXO | Detailed architecture review and voice guidance approved |
| 2026-03-19 | Chief Architect | ADR formalized |
| *Chief Architect | March 19, 2026* |