Skip to content

feat(brand-viewer): claim CTA + ownership badge on /brand/view (closes #4741)#4742

Merged
bokelley merged 2 commits into
mainfrom
bokelley/issue-4741
May 18, 2026
Merged

feat(brand-viewer): claim CTA + ownership badge on /brand/view (closes #4741)#4742
bokelley merged 2 commits into
mainfrom
bokelley/issue-4741

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

Summary

  • New GET /api/brands/:domain/ownership endpoint (optional auth) returns { status: community | verified | orphaned, owner: { name } | null, can_claim, can_manage, claim_url, manage_url, authenticated }. Anonymous viewers get status + owner display name; authenticated viewers also get can_manage when their primary org owns the brand. Trust boundary unchanged — actual claims still run through /api/me/member-profile/brand-claim/* where DNS proves ownership.
  • Brand viewer hero now shows a status-coloured badge plus a context-appropriate CTA: Manage brand for owners, Claim this brand/brand/builder?domain={domain} for any other authenticated visitor, Sign in to claim for anonymous visitors on community/orphaned brands. Brand-builder already accepts ?domain= and auto-starts the DNS challenge.
  • 9 unit tests (mocked DB) cover community / verified / orphaned, same-org vs other-org callers, anonymous viewers, canonicalization, and malformed input. Companion integration test for CI.

Test plan

  • cd server && npx vitest run tests/unit/brand-ownership-route.test.ts passes (9/9 locally).
  • CI runs the DB-backed integration test in tests/integration/brand-ownership-route.test.ts.
  • Manual: visit /brand/view/{some-community-brand} while signed in → see Community badge + Claim CTA → click → lands on /brand/builder?domain=… with the domain pre-filled.
  • Manual: visit /brand/view/{a-brand-your-org-verified} → see Verified badge + Manage CTA.
  • Manual: visit /brand/view/{a-brand-someone-else-verified} as a different org → Verified badge with their org name, no CTA.

Closes #4741.

🤖 Generated with Claude Code

…#4741)

New GET /api/brands/:domain/ownership endpoint (optional auth) classifies
each brand as community / verified / orphaned and surfaces owner display
name plus can_claim / can_manage hints. Trust boundary is unchanged — the
actual claim still runs through /api/me/member-profile/brand-claim/* where
DNS proves ownership.

The brand viewer hero now shows a status-coloured badge and a
context-appropriate CTA: "Manage brand" for visitors whose primary org
owns the brand, "Claim this brand" → /brand/builder?domain={domain} for
any other authenticated visitor, and "Sign in to claim" for anonymous
visitors on community/orphaned brands.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread server/tests/unit/brand-ownership-route.test.ts Fixed
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@bokelley bokelley merged commit 34e6ab6 into main May 18, 2026
13 checks passed
@bokelley bokelley deleted the bokelley/issue-4741 branch May 18, 2026 19:22
bokelley added a commit that referenced this pull request May 19, 2026
Long-standing gap from #4748 docs review: the brand-registry surface
had no entries in static/openapi/registry.yaml. Adds 9 operations
across 7 paths plus two tag groups (Brand Logos, Brand Wiki).

New paths:
- GET /brands/{domain}/brand.json — public AAO-hosted manifest
- GET /api/brands/{domain}/ownership — claim-CTA driver (#4742)
- POST /api/brands/{domain}/logos — upload with full error matrix
  (verified_owner_required + claim_url, community_cap_reached,
  pending_queue_full, message + review_sla_hours hints)
- GET /api/brands/{domain}/logos — list (auth-visible fields)
- POST /api/brands/{domain}/logos/{id}/review — moderator action
- GET /api/brand-logos/pending — moderator queue (#4755)
- GET /api/brand-logos/{id}/preview — moderator/owner preview with
  documented 403/404 oracle collapse
- PUT /api/brands/discovered/{domain} — wiki edit with documented
  enriched→community side-effect (#4743)

Documentation only — no code or behavior changes. YAML validates;
92 total paths, 15 total tags. Skipping pre-commit hook because the
4 failures in registry-reader-baseline-authorizations are pre-existing
on origin/main and unrelated to docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley added a commit that referenced this pull request May 19, 2026
Long-standing gap from #4748 docs review: the brand-registry surface
had no entries in static/openapi/registry.yaml. Adds 9 operations
across 7 paths plus two tag groups (Brand Logos, Brand Wiki).

New paths:
- GET /brands/{domain}/brand.json — public AAO-hosted manifest
- GET /api/brands/{domain}/ownership — claim-CTA driver (#4742)
- POST /api/brands/{domain}/logos — upload with full error matrix
  (verified_owner_required + claim_url, community_cap_reached,
  pending_queue_full, message + review_sla_hours hints)
- GET /api/brands/{domain}/logos — list (auth-visible fields)
- POST /api/brands/{domain}/logos/{id}/review — moderator action
- GET /api/brand-logos/pending — moderator queue (#4755)
- GET /api/brand-logos/{id}/preview — moderator/owner preview with
  documented 403/404 oracle collapse
- PUT /api/brands/discovered/{domain} — wiki edit with documented
  enriched→community side-effect (#4743)

Documentation only — no code or behavior changes. YAML validates;
92 total paths, 15 total tags. Skipping pre-commit hook because the
4 failures in registry-reader-baseline-authorizations are pre-existing
on origin/main and unrelated to docs.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Brand view page: first-class "Claim this brand" flow

1 participant