Spot detail audit: dedup claim provenance tags (1.2.40)

Audited the spot detail surface — status reports, ownership, and
community contributions. It's well-built:

STATUS (useSpotStatus) — a proper trust×recency consensus: each report
weighted by recencyWeight (30-day half-life) × trustWeight (0.2 floor so
everyone counts, trusted users dominate), only the newest report per
reporter counts (no ballot stuffing), reports past 180 days are dropped,
and the parser rejects any unrecognised status value so garbage can't
poison the tally. Single + batch paths both invalidate on publish.

CONTRIBUTIONS — photos (kind 1063) and tag suggestions (kind 1111) are
SEPARATE events referencing the spot via #a, so contributors never touch
the owner's addressable spot event (no clobber). Tag votes dedupe per
contributor. The detail gallery spreads owner images first so the hero
is always the owner's photo when present; community shots are badged.

OWNERSHIP — transfer/claim model is intentional (claiming republishes
under the claimer's own key = their own coordinate; the original
survives, as the UI states). One real rough edge: handleClaim copied ALL
original tags then appended claim-from/claim-reason, so claiming a spot
that was itself a prior claim would STACK multiple provenance pairs on
the event. Fixed by filtering out any existing claim-from/claim-reason
before appending a single fresh pair pointing at the immediate prior
owner. No functional/authorization change.

Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
shakespeare.diy 2026-09-08 00:19:10 +07:00
parent c6e74cc189
commit 6ad112dea3
2 changed files with 6 additions and 3 deletions

View file

@ -1,7 +1,7 @@
{
"name": "skate-spots",
"private": true,
"version": "1.2.39",
"version": "1.2.40",
"type": "module",
"homepage": "https://skate-spots.com",
"scripts": {

View file

@ -148,12 +148,15 @@ export function SpotOwnership({ spot }: SpotOwnershipProps) {
// This creates a new addressable event with the same d-tag but new owner
const eventTags: string[][] = [];
// Copy all original tags except the ones we're changing
// Copy all original tags EXCEPT provenance markers we're about to (re)set.
// Without this filter, claiming a spot that was itself a prior claim would
// stack multiple claim-from/claim-reason pairs on the event.
for (const tag of spot.event.tags) {
if (tag[0] === 'claim-from' || tag[0] === 'claim-reason') continue;
eventTags.push([...tag]);
}
// Add claim reference
// Add a single, fresh claim reference pointing at the immediate prior owner.
eventTags.push(['claim-from', spot.pubkey]);
eventTags.push(['claim-reason', isAbandoned ? 'abandoned' : 'transfer']);