Files
support_backend/specs/005-ai-support/plan.md
T
saqib mirandClaude Sonnet 5 eceb00632d docs: correct 005-ai-support design to reuse the existing ticket state machine
003-ticketing's ticket-state-machine.ts already defines AI_ANALYZING/
AI_TROUBLESHOOTING/AI_VERIFYING/AI_RESOLVED/HUMAN_ESCALATION ticket
statuses, clearly authored anticipating this feature. Corrects the plan
before implementation: AISupportSession.status now drives Ticket.status
through the existing ticketsService.updateStatus (reusing its optimistic
concurrency), instead of an isolated status field the rest of the system
never sees. Also clarifies that knowledge retrieval is an in-process
service call through knowledge's index.ts, not an HTTP loopback.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 16:55:01 +05:30

11 KiB

Implementation Plan: AI Support Agent

Branch: 005-ai-support | Date: 2026-09-02 | Spec: spec.md

Input: Feature specification from specs/005-ai-support/spec.md

Summary

Populate the remaining ai-support submodules (sessions, tools, troubleshooting, escalationknowledge already exists from 004) with the AI reasoning loop: a session starts per ticket, produces a structured, knowledge-grounded diagnosis via a real Anthropic Claude call, applies a configurable confidence-band policy to decide proceed/ask/escalate, executes permission-and-risk-gated tool proposals through a deterministic policy layer (never the model's own judgment), walks an application-controlled runbook step sequence when one matches, and only marks a ticket AI-resolved on real tool-verified evidence. Per explicit product decision, this feature integrates a real LLM provider from the start — no mock/pluggable-interface phase.

Technical Context

Language/Version: TypeScript 5.4 / Node.js 20+.

Primary Dependencies: @anthropic-ai/sdk (new — real LLM calls, research.md), zod (tool input schemas, structured-output schema for diagnosis, admin config validation), Prisma (new models), BullMQ (new AI_SESSION queue/worker — reuses existing queueManager, no new dependency).

Storage: PostgreSQL via Prisma (new AISupportSession, AIDiagnosis, AIInteraction, AIAction, AIActionResult, AIKnowledgeReference, AIConfidencePolicy models per docs/06-database-schema.md, refined in data-model.md). Redis/BullMQ for the async first-turn job, reusing existing infrastructure.

Testing: Vitest — unit tests for the confidence-band decision function, the deterministic tool-policy gate, and the runbook step-advancement logic (all pure, extractable functions, unlike 004's thin-Prisma-query situation); integration tests against a real Postgres and a real Anthropic API call for the full session flow (quickstart.md scenarios) — this is the first feature in this codebase whose integration tests have a real external-network dependency and a real per-run cost, not just Docker-local infra. Per the constitution's Testing gate, this feature also adds the two required standing E2E scenarios: (A) AI resolves directly, (B) AI escalates to human — both were previously unimplementable (no AI session existed) and are added now.

Target Platform: Same Fastify modular monolith. New submodules: src/modules/ai-support/{sessions,tools,troubleshooting,escalation}/ (standard module shape, research.md "Module placement"). New infra: src/infrastructure/ai/ (Anthropic client singleton). New job: src/jobs/ai-session/ (registered in src/bootstrap/queue.bootstrap.ts). Modifies src/modules/ticketing/tickets/service/tickets.service.ts — two hooks: enqueue on ticket creation (research.md "Session triggering"), and end any active AISupportSession inside updateStatus when a human actor moves the ticket (research.md "AISupportSession.status drives Ticket.status", FR-023). Also modifies prisma/schema.prisma. Discovered during this planning pass: 003-ticketing's ticket-state-machine.ts already defines the exact AI_ANALYZING → AI_TROUBLESHOOTING → AI_VERIFYING → AI_RESOLVED / HUMAN_ESCALATION states this feature drives — this feature reuses that state machine and ticketsService.updateStatus directly rather than introducing a parallel one.

Project Type: Backend service — single project.

Performance Goals: Not throughput-sensitive at this phase (one ticket, one session, turns paced by human/customer reply cadence) — but every reasoning call is real LLM latency (seconds), which is exactly why the first turn is queued (research.md) rather than synchronous with ticket creation.

Constraints: MUST NOT let AI free-text influence tool permission/risk/escalation decisions (FR-024, doc 11 §A4); MUST NOT mark a ticket AI-resolved without tool-verified evidence (FR-018); MUST NOT let the model choose or reorder runbook steps (FR-015); MUST cap clarifying questions (FR-009) and, per doc 11 §B2, cap reasoning turns/tool-call iterations per session to prevent a runaway loop.

Scale/Scope: One reasoning agent (not a multi-agent registry), four new submodules, a small fixed tool registry (4 real tools + 1 intentionally-pending-approval high-risk tool). Explicitly excludes: semantic/vector retrieval, product-signal webhook verification, model routing/fallback, cost dashboards, localization, idle-session timeout (see spec.md Assumptions).

Constitution Check

GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.

Principle / Section Check Result
I. SaaS Is the Sole Identity & Access Authority Sessions/diagnoses/actions reference Ticket/Product (SupportHub's own domain) only; no SaaS identity data is duplicated. PASS
II. Configuration Over Hardcoding Confidence thresholds are DB-configurable per product/category (AIConfidencePolicy, FR-005); the model name and reasoning effort are env-configurable (research.md), not inline string literals. PASS
III. Layered Architecture With Enforced Module Boundaries Four new submodules follow the standard shape; troubleshooting reaches knowledge's Runbook data only through knowledge's public index.ts, never a deep import. PASS
IV. AI Recommends, Deterministic Policy Decides This is the central principle this feature exists to implement: evaluateToolProposal (research.md) is the one shared, model-output-blind gate every tool call passes through; confidence-band policy is applied to the diagnosis's numeric score by application code, never by asking the model what it thinks should happen next. PASS
V. Evidence-Based Verification resolved status is guarded on a structured AIActionResult from verifyProductResolution, never on interaction/message content (research.md "Verification and resolution"). PASS
VI. Durable Audit & History Every AIDiagnosis is append-only (never overwritten); every AIAction records its evaluation outcome even when nothing executes; AIKnowledgeReference records exactly what knowledge the AI was shown. PASS
VII. Concurrency-Safe, Durable Job Handling The first-turn job runs through the existing durable BullMQ queueManager (survives a process restart — not an in-memory timer); "one active session per ticket" (FR-001) is enforced as a repository-level check analogous to 003's optimistic-concurrency pattern. PASS
VIII. Problem and Ticket Are Separate, Related Entities AISupportSession attaches to Ticket (per doc 06), and reads Problem for diagnosis context — doesn't collapse the two. PASS
Technology & Platform Constraints Adds exactly one new dependency, @anthropic-ai/sdk — the one genuinely new capability this phase requires; no other new runtime dependency. PASS
Testing gate — AI tool-permission tests Directly required by the constitution's Testing section, not just this feature's own FRs — see quickstart Scenario 3 and tasks.md. Addressed in Phase 3 (US3) tests
Testing gate — two standing E2E scenarios (AI-resolves, AI-escalates) Both were impossible before this feature (no AI session existed anywhere in the codebase) — added here as the constitution requires. Addressed in Phase 6 (Polish)

No violations requiring Complexity Tracking justification. The one deliberately-incomplete piece (overrideTicketPriority staying pending_approval forever, with no approval UI yet) is an explicitly documented known limitation, not a silent gap — same class as fastify.authenticate.

Post-Design Constitution Re-check

All gates above remain PASS after Phase 1 design (research.md, data-model.md, contracts/, quickstart.md). Worth calling out explicitly against Principle IV: the two-call design (classify+diagnose, then separately reason/act — research.md) means the confidence-band policy sits in application code between two model calls, not inside a prompt instruction hoping the model applies its own policy correctly — this is what makes Principle IV a mechanical guarantee here rather than a hope.

Project Structure

Documentation (this feature)

specs/005-ai-support/
├── plan.md              # This file
├── research.md          # Phase 0 output
├── data-model.md        # Phase 1 output
├── quickstart.md         # Phase 1 output
├── contracts/            # Phase 1 output
└── tasks.md              # Phase 2 output (/speckit-tasks — not created here)

Source Code (repository root)

supporthub-api/
├── prisma/
│   └── schema.prisma                       # MODIFIED — add AISupportSession, AIDiagnosis,
│                                              AIInteraction, AIAction, AIActionResult,
│                                              AIKnowledgeReference, AIConfidencePolicy
├── src/
│   ├── infrastructure/
│   │   └── ai/                             # NEW — Anthropic client singleton, model/effort
│   │       └── anthropic.client.ts           config resolved from env (research.md)
│   ├── jobs/
│   │   └── ai-session/                     # NEW — worker for the queued first-turn diagnosis
│   │       └── index.ts
│   ├── bootstrap/
│   │   └── queue.bootstrap.ts               # MODIFIED — register the new AI-session worker
│   └── modules/
│       ├── ticketing/
│       │   └── tickets/
│       │       └── service/
│       │           └── tickets.service.ts   # MODIFIED — enqueue AI_SESSION job on new ticket
│       └── ai-support/
│           ├── knowledge/                   # existing (004) — untouched
│           ├── sessions/                    # NEW
│           │   ├── controller/ routes/ schema/ repository/ service/ types/ mapper/
│           │   │   constants/ index.ts
│           ├── tools/                       # NEW
│           │   ├── controller/ routes/ schema/ repository/ service/ types/ mapper/
│           │   │   constants/ index.ts       # constants/tool-registry.ts — the fixed tool set
│           ├── troubleshooting/              # NEW
│           │   └── service/ types/ index.ts  # no own routes — invoked by sessions' service
│           └── escalation/                   # NEW
│               └── service/ types/ index.ts  # no own routes — invoked by sessions' service
└── tests/
    ├── unit/ai-support/                     # confidence-band decision, tool policy gate,
    │                                          runbook step-advancement (pure functions)
    └── integration/                         # full session flow against real Postgres + real
                                               Anthropic API (quickstart.md scenarios)

Structure Decision: Single project. troubleshooting and escalation are internal-only submodules (service logic sessions calls through their index.ts, per Principle III) rather than exposing their own routes — neither has an independent HTTP surface in spec.md's requirements; both are invoked as part of a session turn. This mirrors how messages/ attachments in 003-ticketing are separate modules from tickets but still ultimately driven through the same request.

Complexity Tracking

No constitution violations — table intentionally omitted.