# Feature Spec: Ticket File Attachments

**Status:** Spec — under review  
**Requested by:** Ramon  
**Original request:** "feature add to add box to add up to 3 files to a ticket with max file size each 10mb"

---

## Overview

Line managers should be able to attach files (photos, PDFs, test reports) when submitting a ticket or adding a clarification. This is a natural fit for the manufacturing quality context — a photo of a defective component, a scan of an inspection record, or a test report extract is often more informative than a text description alone.

The request is simple on the surface, but file handling is one of the most complexity-multiplying features in a web application. This document breaks down the design space, the tradeoffs, and a recommended phased approach.

---

## Why this feature is worth doing carefully

File handling expands the security surface area (malware, path traversal), adds storage cost and lifecycle management, introduces pipeline reliability concerns, and creates a set of edge cases (slow connections, browser crashes mid-upload, corrupt files, exotic MIME types) that are hard to test exhaustively. None of these are reasons to avoid the feature, but they are reasons not to rush it.

Additionally, the question of *what to do with attached files* is not straightforward once AI processing is involved. See the processing paths below.

---

## Constraints (from Ramon's request)

- Up to **3 files** per ticket submission
- Maximum **10 MB per file**
- The technical spec already defines a `ticket_attachments` table and a two-step upload flow (§9.4); the schema is ready, the implementation is not

---

## Recommended file types

Not all file types are equally useful or safe. For the quality manufacturing context, a sensible initial allowlist:

| Type | MIME | Rationale |
|---|---|---|
| JPEG / PNG / WEBP | `image/*` | Photos of defects, equipment, labels — the most common case |
| PDF | `application/pdf` | Test reports, SOPs, inspection records |
| DOCX | `application/vnd.openxmlformats...` | Written reports |
| XLSX / CSV | spreadsheet types | Batch data, test logs — harder to process meaningfully |

Explicitly **excluded**: executables, ZIP/archive files, HTML. A strict MIME-type allowlist enforced server-side (not just by the browser `accept` attribute) is required regardless of phase.

---

## The three processing paths

This is the key design question. The answer affects token cost, implementation complexity, and the value delivered to the expert and the AI pipeline.

### Path 1 — Store and forward (no AI involvement)

Files are uploaded, malware-scanned, stored in object storage, and made available for the expert to download. The AI pipeline ignores them entirely.

**What the expert gets:** a download link on the ticket detail view.  
**What the AI gets:** nothing — the AI summary, clarification questions, and draft response are based only on text fields.

**Pros:**
- Straightforward to implement
- No token cost
- No processing pipeline to maintain
- Delivers immediate value to the expert who can see the original file

**Cons:**
- The AI cannot help with the content of the file at all
- The expert has to open and read files manually
- A photo of a defective component that contains important diagnostic information is invisible to the completeness agent — it might still ask the line manager to describe what they see

**Verdict:** A solid first phase that delivers real value with manageable complexity. Not the end state, but a defensible place to start.

---

### Path 2 — Send raw file content to the LLM

Files are passed directly into the LLM prompt context alongside the ticket text — PDFs as extracted text, images via a vision model.

**The token cost problem.** A 10 MB PDF could be hundreds of pages. Extracted text from a dense regulatory document could easily be 50,000–150,000 tokens. At current Claude Sonnet pricing, that is roughly $0.15–$0.45 per file in input tokens alone — potentially $1.35 per ticket if all three files are dense. At scale, this is unsustainable and would make the per-ticket economics unworkable.

Images are more manageable — a JPEG typically costs 1,000–1,500 tokens via a vision model — but still adds up at volume.

**Verdict:** Not recommended as a default path. Suitable only for small, focused documents (e.g. a one-page data sheet). Token budgets would need hard caps and the cost implications need to be understood before enabling this.

---

### Path 3 — Extract, chunk, and filter before the LLM (recommended end state)

A pre-processing pipeline runs before the AI completeness or triage agents see the attachment content:

1. **Extract** — convert the file to plain text and/or image regions
   - PDF → text extraction (`pdf-parse` or similar; fallback to OCR for scanned PDFs)
   - Images → OCR for text content; pass image directly to a vision model for visual content
   - DOCX → text extraction; XLSX → structured text summary of data ranges

2. **Chunk** — split the extracted content into semantic chunks (paragraphs, sections, table rows)

3. **Filter** — score chunks by relevance to the ticket's text fields and discard low-signal content
   - Option A: embed chunks and take the top-K by cosine similarity to the ticket body
   - Option B: a cheap/fast LLM pass (e.g. Haiku) that reads the chunks and returns only the relevant sections — adds one LLM call but produces cleaner output

4. **Inject** — pass only the filtered, relevant excerpt into the main pipeline alongside the ticket text, clearly labelled as attachment-derived content

**What this solves:** a 10 MB PDF becomes maybe 500–2,000 tokens of relevant extracted content, not 100,000 tokens of raw text. The AI gets signal, not noise.

**Pros:**
- Sustainable token economics
- AI can genuinely help with file content (completeness agent can ask better questions, draft agent can reference the document)
- Relevant excerpts are surfaced to the expert alongside the AI summary

**Cons:**
- Significantly more complex to build and maintain
- Extraction quality varies by file type; scanned PDFs and complex Excel layouts are difficult
- Chunking and relevance filtering introduce their own failure modes

**Verdict:** The right end state, but not where to start. Build Path 1 first, then layer on Path 3 when there is evidence that experts need AI to reason about attachment content.

---

## Phased implementation plan

### Phase 1 — Store and forward (scope: M)

**What ships:**
- File picker on the submission form (up to 3 files, 10 MB each, allowlisted MIME types)
- Two-step upload: client requests a signed upload URL → uploads directly to object storage → server records metadata in `ticket_attachments`
- Malware scan on upload; blocked files show an error state, not a download link
- Expert sees attachment thumbnails/links on the ticket detail view
- AI pipeline unchanged — attachments are not passed to any agent

**Schema:** `ticket_attachments` is already defined. No migration needed beyond what is already planned.

**Open questions to resolve before building:**
- Which object storage provider? (Vercel Blob, AWS S3, Cloudflare R2 — R2 has no egress fees which matters for frequently-downloaded files)
- Retention policy: how long are attachments kept after a ticket is archived?
- Does the line manager see their own uploaded files in the ticket view?

---

### Phase 2 — AI-aware attachments: images and short documents (scope: M–L)

**What ships:**
- Images passed to the vision model as part of the completeness assessment (Hook 1) and response drafting (Hook 5)
- PDF text extraction with a hard token cap (e.g. first 4,000 tokens of extracted text) — cheap to implement, avoids the worst cost scenarios while adding some value
- Extracted text clearly labelled in AI context as "from attachment: [filename]" to prevent hallucination confusion

**This is a deliberate halfway point.** It adds AI value for the most common file types (photos, short reports) without building the full chunking/filtering pipeline.

---

### Phase 3 — Smart extraction pipeline (scope: L)

**What ships:**
- Full chunking and relevance filtering pipeline as described in Path 3 above
- Per-attachment token usage logged in `ticket_processing_runs` for cost monitoring
- Relevant excerpt surfaced to the expert in the ticket detail view alongside the AI summary
- Configurable per-tenant: `max_attachment_tokens` in tenant config

**Only build this if Phase 2 reveals that token costs are a problem or that experts are getting low-quality AI output because relevant information was in a large document that got truncated.**

---

## Roadmap placement

The technical spec already lists attachments as item **4.1–4.3** with scope **L** and the note "defer until a user asks for it." Ramon's request is that signal.

Recommended revision:
- **Phase 1** moves to the "Up next" section — scope M, can be parallelised with other work
- **Phase 2** moves to "Later / when there's user demand" — scope M–L
- **Phase 3** stays at the bottom — scope L, only warranted by evidence from Phase 2

---

## Storage cost note

At 3 files × 10 MB × N tickets per day, storage is inexpensive at early customer scale (gigabytes per month, not terabytes). The costs that matter more are:
- **Egress** when experts download files — choose a provider with low/no egress fees (Cloudflare R2 is the current best option for this)
- **LLM tokens** in Phase 2/3 — log per-ticket attachment token usage from the start so you can see the cost impact as it accumulates
- **Malware scanning** — most providers charge per scan; at low volume this is negligible

---

## Summary

| Path | AI value | Complexity | Token cost | Recommended phase |
|---|---|---|---|---|
| Store and forward | None (expert only) | Low | None | Phase 1 — build now |
| Raw file to LLM | High but wasteful | Low | Very high | Not recommended |
| Extract + chunk + filter | High and efficient | High | Low | Phase 3 — after evidence |
| Extract with hard token cap | Medium | Medium | Controlled | Phase 2 — after Phase 1 |
