Undo (latest verdict wins):
- MODERATION_LABEL_CLEARED = 'cleared' — the neutral undo verdict.
- useAdminModeration no longer UNIONS labels (which made a hide permanent in the
UI); it keeps the NEWEST verdict per target (created_at, with device-local
verdicts overlaid by timestamp), so 'cleared' supersedes an earlier
'hidden'/'verified'. useAdminClear() publishes it.
- Local mirror is now a verdict map (skatespots_local_admin_verdicts_v1: key ->
{verdict, at}) with migration from the legacy hidden/confirmed arrays.
- useSkateSpots(limit, { includeHidden: true }) — ADMIN TOOLING ONLY: without it
a hidden spot leaves the list, so a verdict could never be undone. The review
queue uses it, keeps actioned rows (Hidden/Confirmed badge + Undo), and sorts
un-actioned first.
Community photos as a signal:
- useBatchSpotPhotos: ONE #a query for every spot (the single-spot hook already
queried by #a, so batching is free), counting only photos from people OTHER
than the mapper. Folded into the 'no community signals' flag.
Verified by seeding local verdicts: none -> flags + Confirm/Hide; hidden ->
Undo; verified -> Undo; cleared -> back to neutral. tsc/eslint clean, 101 tests,
smoke PASS.
- MODERATION_LABEL_VERIFIED: the positive counterpart to 'hidden'. Both labels
are fetched in ONE query; useAdminModeration now exposes verifiedCoords/
verifiedIds/isVerified() with a local mirror (addLocalConfirmed/
getLocalConfirmed). useAdminVerify() publishes it (shares the publish path
with useAdminHide).
- Review queue: a human review can now CONFIRM a spot, not just hide it. A
confirmed spot clears from the queue and gets a 'Verified by SkateSpots' badge
on the spot page (staff verdict, shown separately from the community badge).
- Two more signals: mapper trust (useBatchTrustScores) and whether any skater has
filed a status report (useBatchSpotStatus reporterCount) -> 'mapper not in your
web of trust' / 'no community signals'. The latter only fires alongside another
flag so a brand-new real spot isn't punished for being new.
- Flag logic moved to src/lib/spotQuality.ts so the page and AdminReviewCard share it.
Verified: the queue lists the airport entry with suspicious name + no photo + no
community signals, shows mapper trust, and offers Confirm/Hide/Not a skate
spot/Look at it.
DELETE HANG (real fix):
The admin takedown required TWO sequential Amber approvals (kind:5
deletion + kind:1985 hide label) and awaited both. On mobile the second
Amber prompt often doesn't resurface, so the button sat on "Deleting…"
until the 90s signer timeout. Now:
- Only the kind:5 deletion is awaited (the required action).
- The target is recorded in a local optimistic-hide store immediately, so
the app hides it on this device the instant kind:5 succeeds — no waiting
on a second signature.
- The cross-device kind:1985 label is fired in the BACKGROUND (not
awaited); a second approval, if needed, happens after the dialog closes.
- The whole mutation is wrapped in a 40s hard timeout so the spinner can
never hang indefinitely, whatever the signer/relay does.
- useAdminModeration merges the local optimistic set so hides are instant
and survive reload.
NOTIFICATION BELL UI:
- Brand-lime (#ccff00) glowing badge with ring, replacing the flat teal dot.
- Bell wiggles + soft glow pulse when there are unread items (new `wiggle`
keyframe).
- Gradient header with an icon chip, inline count, and a pill "Mark read"
button / "All clear" state.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>