# Flow F1 — Annotation Create Cross-reference: `system-flows.md` → Flow F1. ## Sequence (verified against `Services/AnnotationService.cs`) ```mermaid sequenceDiagram autonumber participant Caller as Detections / UI participant Ctrl as AnnotationsController (01) participant Svc as AnnotationService (01) participant Path as PathResolver (06) participant DB as PostgreSQL (06) participant FS as Filesystem participant Evt as AnnotationEventService (02) participant Q as annotations_queue_records (DB / 02) Caller->>Ctrl: POST /annotations (CreateAnnotationRequest, JWT ANN) Ctrl->>Svc: CreateAnnotation(request, userIdFromJwt) alt request.Image bytes provided Svc->>Svc: ComputeHash (XxHash64 over sampled bytes) -> id Svc->>FS: write {id}.jpg under images_dir Svc->>DB: SELECT media WHERE id = :id opt media row missing Svc->>DB: INSERT media (Image, MediaStatus.New, ...) end else MediaId provided Svc->>DB: SELECT media WHERE id = :MediaId (404 if missing) opt source media file exists & target image missing Svc->>FS: copy media.Path -> images_dir/{id}.jpg end end Svc->>DB: INSERT annotations Svc->>DB: BulkCopy detection rows Svc->>FS: write {id}.txt (YOLO label) under labels_dir Svc->>Evt: PublishAsync(AnnotationEventDto) Svc->>DB: SELECT system_settings (FirstOrDefault) alt SilentDetection != true Svc->>Q: FailsafeProducer.EnqueueAsync(db, id, QueueOperation.Created) end Svc-->>Ctrl: Annotation Ctrl-->>Caller: 201 Created (Location: /annotations/{id}) ``` ## Flowchart ```mermaid flowchart TD start([POST /annotations]) --> auth{JWT valid + ANN claim?} auth -->|no| rej401([401 / 403]) auth -->|yes| input{bytes or MediaId?} input -->|neither| arg([400 ArgumentException]) input -->|bytes| hash[ComputeHash sampled XxHash64 -> id] input -->|MediaId| lookupMedia[SELECT media WHERE id = MediaId] lookupMedia -->|missing| nf404([404 KeyNotFound]) lookupMedia -->|exists| copyImg[copy media.Path to images dir if missing] hash --> writeImg[write {id}.jpg] writeImg --> mediaRow[INSERT media if absent] mediaRow --> writeDb copyImg --> writeDb[INSERT annotations + BulkCopy detections] writeDb --> writeLabel[write {id}.txt YOLO label] writeLabel --> sse[PublishAsync SSE event] sse --> readSettings[SELECT system_settings] readSettings --> silentChk{SilentDetection?} silentChk -->|yes| ok([201 Created]) silentChk -->|no| outbox[FailsafeProducer.EnqueueAsync Created] outbox --> ok writeImg -->|IOException| err500([500 via ErrorHandlingMiddleware]) writeDb -->|DB error| err500 writeLabel -->|IOException| err500 outbox -->|DB error| err500 ``` ## Notes - Image hashing is `XxHash64` over a **sampled** input (length prefix + head/middle/tail 1KB) for inputs > 3072 bytes. See ADR-004 in `architecture.md` for collision implications. - The implementation is **not transactional across FS + DB + outbox**. Partial failure can leave orphan files or unsent outbox rows. Captured in `system-flows.md` → Open Behavioral Questions §4. - `Update`, `UpdateStatus`, `DeleteAnnotation` paths do **NOT** publish SSE or enqueue outbox today. Captured in `system-flows.md` → Open Behavioral Questions §1. - Outbox row is consumed asynchronously by Flow F4 (`FailsafeProducer`).