# TechTitans — AI voice repair intake: real-provider verification

**Branch:** `techtitans/repair-r1`. Scope: verify real voice transcription + natural-language repair draft
preparation on the existing Repair R1 architecture. **No repair wizard / CRM / device registry was
rebuilt; no source was modified** (no defect found) — this is a verification record + one new e2e spec.

## 1. Existing implementation — inspected (all sound & honest)

- **Audio recording** `components/repairs/RepairVoiceCapture.tsx` — records in-memory only (never
  persisted), releases the mic on stop/unmount; **Transcribe** appears only after a recording and is
  **disabled with an honest note when transcription isn't configured** (503 handled gracefully).
- **Transcription adapter** `app/AI/Repair/Openai/OpenAiSpeechTranscriber.php` — OpenAI Whisper
  (`POST {base}/audio/transcriptions`, multipart `file` + `model` + `response_format=json`, optional
  `language`); fails closed (503) without a key; upstream errors → 502; returns plain text only.
- **Draft extractor** `app/AI/Repair/Openai/OpenAiRepairDraftExtractor.php` — `chat/completions`,
  strict JSON, `temperature 0`, coerced to the contract; **never invents** imei/serial/model/diagnosis/
  price/technician/warranty; captures customer/phone/email/repair_type/technician/accessories ONLY when
  stated; relative dates never resolved.
- **Customer + device matching** `lib/repairs/intakeMatching.ts` — deterministic match against the
  existing CRM; a reliable unique match is used, ambiguous matches prompt confirmation, staff can correct.
- **Technician suggestion** — reuses `GET repairs/technicians` (active, location-eligible staff only);
  never auto-creates a technician; an unmatched name is left Unassigned.
- **Review/approval** `components/repairs/SmartIntakePanel.tsx` + the shared wizard — the prepared draft
  fills the wizard for edit; explicit "Create repair" is required; a `mock` driver is badged
  "Development AI (mock) — review every field" (never presented as real).

### Was real vision verified first? No — honest status
Real **vision** recognition was **NOT** verified in the prior task: it is **blocked (no API key)**. The
vision path was verified *sound* and label extraction (colour/serial) was added + mock-tested, but live
recognition quality remains **unverified**. The same single blocker (no key) applies here to transcription.

## 2. Provider configuration status (checked without exposing credentials)

- `.env` has **no** `AI_REPAIR_TRANSCRIPTION_*` / `AI_REPAIR_DRAFT_*` / `AI_REPAIR_VISION_*` /
  `OPENAI_API_KEY` (checked by name only). OS env unset.
- Live `GET /repairs/intake/capabilities` → **`transcription: {configured:false, driver:none}`**,
  **`draft: {configured:true, driver:mock}`**, `vision: {configured:false}`.
- Live `POST /repairs/intake/transcribe` → **503 `ai_repair_transcription_not_configured`**
  (`required_env: [AI_REPAIR_TRANSCRIPTION_DRIVER=openai, AI_REPAIR_TRANSCRIPTION_API_KEY]`).

**Real voice transcription is BLOCKED (no key). Natural-language draft preparation currently runs on the
deterministic DRAFT MOCK — a dev stand-in, NOT a real provider** (clearly badged in the UI).

## 3. Tests — real vs mock, clearly separated

| Capability | Status | Evidence |
|---|---|---|
| Real Whisper transcription | **BLOCKED — not run** (no key) | adapter inspected; live transcribe → 503 |
| Real OpenAI draft extraction | **BLOCKED — not run** (no key) | adapter inspected; driver is `mock`, not `openai` |
| Draft extraction (deterministic mock) | **Verified** | backend `RepairIntakeAiTest` + live browser |
| Customer/device/technician matching, review, no-invention | **Verified** (mock-driven) | frontend unit + e2e |

- **Backend** `php artisan test --filter=RepairIntakeAiTest` → **12 passed (76 assertions)** — transcription
  503, transcribe mock, draft mock (no forbidden fields), stated customer/technician/accessory capture,
  capabilities honesty, `manage` permission.
- **Frontend** `vitest` `RepairVoiceCapture.test.tsx` + `intakeMatching.test.ts` +
  `NewRepairSmartIntake.test.tsx` → **29 passed**.
- **Live browser (Playwright, techtitans)** `e2e/repair-voice-intake.spec.ts` → passed. Captured the real
  capabilities payload (`draft=mock`, `transcription=none`) and the real mock draft output. Screenshots in
  `frontend/test-results/repair-voice/`:
  - `voice-landing.png` — Smart intake with the **"Development AI (mock)"** badge; the Voice recorder with
    **Transcribe disabled / "transcription isn't configured"**.
  - `voice-prepared-review.png` — one instruction ("Dinesh… Apple Watch… doesn't turn on… assign to John")
    → wizard step 1 with the honest notice **"Couldn't match technician 'John' — left Unassigned. Matched
    customer by name: Dinesh"**, Customer=Dinesh (editable), device Category=watch / Brand=Apple populated,
    **Model/IMEI/Serial/Colour empty (never invented)**. Nothing created until approval.

## 4. Verified behaviours (Tasks 3–6)

- NL instruction → structured, validated draft (device, fault, priority, technician captured only when
  stated; model/serial never invented).
- **Customer match:** reliable unique match used (Dinesh); ambiguous → confirmation; staff can change.
- **Technician:** unmatched name left **Unassigned** — never auto-created/assigned.
- **Review/approval:** all fields editable; explicit create required; idempotent create + attachment
  persistence reuse the existing R1 APIs (covered by the photo-first + R1 suites).

## 5. Enable + verify real voice (exact settings — server-side only)

`backend/.env` (never commit keys):

```
AI_REPAIR_TRANSCRIPTION_DRIVER=openai
AI_REPAIR_TRANSCRIPTION_API_KEY=sk-...   # Whisper-capable key
# optional: AI_REPAIR_TRANSCRIPTION_MODEL=whisper-1  (default)

# To also run REAL (not mock) draft extraction:
AI_REPAIR_DRAFT_DRIVER=openai
AI_REPAIR_DRAFT_API_KEY=sk-...
# optional: AI_REPAIR_DRAFT_MODEL=gpt-4o-mini  (default)
```

Then `php artisan config:clear` + restart `php artisan serve --host=127.0.0.1 --port=8010`.
`capabilities.transcription.configured` flips true and the Transcribe button activates; the draft badge
changes from "Development AI (mock)" to "AI-assisted". Provide a short non-sensitive sample audio and the
full Record → Transcribe → prepare → match → review → create path can be verified end-to-end on the real
provider.

## 6. Limitations / notes

- Real transcription + real draft extraction are **unverified — blocked on credentials** (the single
  blocker). All "verified" results above are the deterministic mock, labelled as such — not a substitute
  for real-provider testing.
- Audio is held in memory only and never persisted (unless `ai_repair.store_audio` is explicitly set).
- Credentials stay server-side; none present in `.env`, logs, bundles or Git. Not pushed or deployed.
