Skip to content

Managing visual tags

In the admin: Media library — visual-tag review lives on the library cards (#1370; the old /admin/media-tags screen now redirects here).

What/why: Content & media — what this screen manages and why it exists.

Purpose: review the AI-proposed tags that describe what each image depicts — accept or reject them — so imagery is classified against the same controlled vocabulary the media library uses, without an analyzer ever silently changing the taxonomy.

Since #1370 the review is part of the media library (/admin/media):

  • every card shows its AI visual tags as status-coded chips (proposed / rejected) next to a real thumbnail, and a proposed chip carries inline ✓/✕;
  • select assets (the same multi-select used for bulk tagging) and use Accept AI tags / Reject AI tags in the bulk bar to decide every proposed tag on the selection — across all axes the AI proposes — in one action;
  • each bulk decision is provenance-stamped (AI-proposed → human-decided, dated) and undoable per batch: the status line offers "Undo this batch", which reverts only what is still in the state the batch set (a tag someone re-decided afterwards is never clobbered) — an accept batch's undo removes the promoted classification tags and re-seeds the proposals;
  • review pages with the library (server-side pagination, #375) — no full-table load at 18.8k scale;
  • the coverage filter (#1371) drives the queues: AI-proposed, unreviewed pages straight to the review backlog; Untagged is the classification gap (project membership doesn't count as classification); Not in a shot group feeds the bulk-suggest workflow — each composes with project scope and sort;
  • Run AI analysis (page header) runs the pass that proposes tags (#1188 vision adapter when egress is enabled, deterministic stub otherwise).

When to use

  • After an analysis pass has run and proposals are waiting for review.
  • Auditing which visual tags are live on imagery, or re-running the pass after new media has been synced.

Prerequisites

  • Admin access with the edit_data capability (any data_editor or admin role).
  • Referenced media to analyze — synced from nanawall.com (see Managing media).

Steps

  1. Open Admin → Visual tags (/admin/media-tags). The header counts what's to review, live, and rejected.
  2. To generate proposals, click Run analysis pass. The pass is cost-bounded — it analyzes a batch of images per run and defers the rest (the flash message reports how many were analyzed, unchanged, and deferred); re-run it to work through the catalog. Nothing is applied silently — every proposal lands as a proposed row.
  3. For each proposal:
    • Accept — the tag is promoted to a real classification tag (#1805): it appears on the asset's Classification tab with an ai provenance badge, and the audit trail records who accepted it and when.
    • Reject — the proposal is remembered and never re-proposed for that image/tag.
  4. Every accept/reject and each pass run is recorded in the audit trail.

Behavior guarantees

  • Proposals are never live until accepted; rejected proposals are never re-proposed.
  • Accepted proposals live in media_tags — the single live store (#1805) — with source='ai'; the review table holds only the inbox and the rejection memory.
  • The pass is idempotent — an image already analyzed against its current state is skipped unless you force a re-analysis, so a re-run never churns reviewed tags.
  • Provenance is explicit: a proposal is ai; once accepted, the live classification tag keeps source='ai' so its origin never disappears, and the audit trail records who accepted it and when.

Running the pass from the CLI

For a large batch, run the pass against the local dev database directly:

npm run media:analyze              # up to the default budget
npm run media:analyze -- --max 2000 # a bigger batch
npm run media:analyze -- --force    # re-analyze every image

It prints a cost/throughput report (assets scanned, analyzed, unchanged, deferred; tags proposed; embeddings written).

How similarity works

Each analyzed image also gets a stored similarity embedding, so "assets like this" is a bounded cosine query over the embedding store. Per the decision on this build, the shipped analyzer and embedder are deterministic stubs behind a pluggable adapter seam — a real vision model is a later one-adapter swap that leaves the review, provenance, and similarity paths unchanged. The review workflow mirrors the content-tags review pattern.