Media Auto-Filer

Service: services/media_auto_filer.py in entirely-api. Auto-files inbound WhatsApp photos (Rob → /openclaw-media/inbound/) onto projects by matching photo timestamps/caption hints to Darius’s site-visit windows. Runs every 30 minutes 24/7 (scheduler INTERVAL_TASKS → ARQ worker).

Ambiguous-cluster handling (2026-07-21, api 0eada59 / portal 327180f)

  • Notification dedup: clusters the matcher can’t place notify Dustin once when first seen, then at most daily, via Redis keys media_autofiler:ambig_notified:<sha256[:16] of sorted filenames> with 24h TTL. Previously re-notified every 30-minute run (~42 identical pings over 21h on 2026-07-20/21 for one stuck photo). Redis failure fails open. Keys cleared on manual assign/dismiss.
  • Portal resolve UI: Monitoring → Pending Media (/pending-media, up_owner/up_admin). Backed by /portal/field-media/* routes (routes/portal_field_media.py): GET /pending (clusters + candidate visits + byte-identical duplicate probe against project_files + recent Darius visits), GET /inbound/{filename} (thumbnail), POST /assign (guarded by echoed filenames; 409 on stale cluster view), POST /dismiss (deletes inbound copy + sidecar for junk / already-filed re-sends).
  • Cross-project duplicates: save_file refuses to write content that already exists on a different project; run_manual_assignment now surfaces this as an explicit error (assign to the holding project or dismiss) instead of falsely reporting “filed”.

Incident that drove this (2026-07-20/21)

Darius re-sent a photo byte-identical to one already filed on MAN-9124; the cross-project-duplicate guard correctly refused to file it to MAN-9128, but with no resolve path it re-notified every 30 minutes until manually quarantined. The dismiss action now handles this in one click.