Likeness Review History
Likeness Review History
This flow extends Contested Likeness Review with CS-facing visibility: a read-only history of every resolved likeness decision, and structured rejection reasons captured at resolve time.
The Strip likeness page has two tabs: Pending (the actionable queue documented in the contested review flow) and Resolved / History (this flow). Both are served by apps/strip/internal/app/handlers/likeness_reviews.go; the history tab is /likeness-reviews?tab=resolved.
Architecture
sequenceDiagram participant Strip as Strip (Resolved / History tab) participant Sirloin participant Images as characters.reference_dataset_images participant Audit as strip_audit_logs
Strip->>Sirloin: StripListLikenessHistory(search, cursor, limit) Sirloin->>Images: Page characters with resolved likeness images Sirloin->>Audit: Load events for those images (entity_type = REFERENCE_DATASET_IMAGE) Sirloin-->>Strip: characters -> slots -> chronological events Note over Strip: Read-only. Approve/reject only happens on the Pending tab.
Strip->>Sirloin: StripResolveLikenessReview(rejected, rejection_reason, note) Sirloin->>Images: status = REJECTED, rejection_reason, rejection_note Sirloin->>Audit: Record admin decision with reason metadataBehavior
- The history tab groups resolved images by character, then by upload slot (
face_frontal,full_body,full_body_any,*_nsfwvariants). Slots render in the upload-form order — face first — regardless of upload recency, labelled as in brisket: “Face & full chest area”, “Full body front”, “Full body”. Each slot shows its current status (SELECTED/REJECTED/SUPERSEDED) and a chronological timeline of events (UPLOADED,APPROVED,REJECTED,SUPERSEDED) with reviewer email and timestamp. - Rejecting an image in the Pending tab now requires a
StripLikenessRejectionReason: needs proof of creation, real images of someone else, unusable for generation, or other (with a free-text note). Bulk reject collects a reason per image. - Re-uploading a replacement into a slot soft-deletes the previous image row, but its decision trail stays visible in history — the listing ignores
reference_dataset_images.deleted_aton purpose. Only deleting the character removes its history from the tab. - Pending-tab groups whose character already has resolved decisions (
StripLikenessReviewItem.has_history) render a “History” button deep-linking to the history tab pre-filtered to that character. - Search matches character name, character ID, user ID, or user email. Results are cursor-paginated per character.
- The history view performs no mutations; it exists so CS can answer “what happened to this upload and why” without engineering help.
Persistence Notes
- Migration
122_reference_dataset_image_rejection_reason.sqladdsrejection_reasonandrejection_notecolumns tocharacters.reference_dataset_images, so the latest decision metadata lives on the image row itself. - The per-image timeline is reconstructed from
strip_audit_logs(entity_type = REFERENCE_DATASET_IMAGE, ordered bycreated_at). Reviewer identity and historical reasons come from the audit rows, not the image table, so older decisions keep their context even after a slot is re-uploaded.