Ctrl K
ComunidadGuías…

docs/adr/0071-sedo-listing-provider.md

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# ADR-0071: Sedo Listing Provider and Sell-Side Correctness Fixes

## Metadata

| Field | Value |
|-------|-------|
| **Status** | Superseded by [ADR-0082](0082-removal-of-unimplemented-features.md) |
| **Date** | 2026-09-07 |
| **Authors** | Alessio Brillo |
| **Deciders** | Alessio Brillo |
| **Supersedes** | N/A |
| **Relates to** | ADR-0004, ADR-0006, ADR-0070 |
| **Project** | DOMINUS |

## Context

ADR-0070 unlocked Afternic behind the single-active-provider switch and
closed the trademark-gate bypass on the publish path. Three sell-side
defects remained:

1. `sedo` (and `godaddy`) are accepted by the `MarketplaceName` type, the
   `0024_create_listings` CHECK constraint, `LISTING_DEFAULT_MARKETPLACE`
   and the listings API allow-list — but no provider implements them, so a
   `sedo` draft published through the active Dan/Afternic provider was
   silently misrouted to the wrong marketplace.
2. `ManualListingProvider.isAvailable` is always `true`, so
   `ListingManager.listOnMarketplace()` took the remote branch and called
   `createListing()` → fresh `INSERT` → `UNIQUE(domain, marketplace)`
   violation → the draft was stuck at `pending`.
3. All three HTTP adapters looped `while (all.length < total)` with no page
   cap (infinite loop when `total` grows) and used bare `parseInt()` on
   remote string ids (`sd-1001` → `NaN`), silently breaking the sync
   offer linkage.

## Decision Drivers

1. **Provider abstraction (ADR-0004)** — a new marketplace is one adapter
   file plus factory/config wiring; zero core-logic changes.
2. **Trademark gate is non-negotiable (ADR-0006)** — unchanged; the new
   adapter reuses the existing pre-insert/pre-publish checks.
3. **Fail loudly, never misroute** — a marketplace label that does not
   match the active provider must be a visible 400, not a silent publish
   elsewhere.
4. **Zero-cost discipline** — no new dependencies; `fetch` + existing
   retry/error types only.

## Considered Options

### Option A: Sedo adapter + publish-path guards, single active provider (Chosen)

- New `SedoListingProvider` mirrors `AfternicListingProvider` (Bearer
  auth, paginated sync, status maps). `LISTING_PROVIDER` gains `sedo`;
  `SEDO_API_KEY` / `SEDO_API_URL` added. Without a key
  `isAvailable=false` and the manager degrades to local tracking,
  identical to Dan/Afternic semantics. Remote schema is adapter-shaped
  (refined against live credentials later), same caveat as ADR-0070.
- Shared `remote-id.ts`: `safeRemoteNumericId()` (numeric ids stable,
  string ids → deterministic hash, never NaN) and `MAX_SYNC_PAGES = 50`
  adopted by all three HTTP adapters; truncation surfaces as a sync
  error string plus a warn log.
- `ListingManager.listOnMarketplace()`: the local-only path triggers on
  `provider.name === 'manual'` (not `isAvailable`), fixing the
  UNIQUE-violation stuck-pending bug; a draft whose marketplace differs
  from the active non-manual provider throws `LISTING_MARKETPLACE_MISMATCH`
  (mapped to HTTP 400 on `POST /:id/publish`).
- `recordOffer()` rejects non-finite/`<= 0` amounts (defense in depth;
  the API route already validates).

**Disadvantages:** no parallel Dan+Afternic+Sedo listing (same single-
provider trade-off as ADR-0070); `godaddy` remains label-only.

### Option B: Multi-marketplace fan-out registry

Publish one domain to several marketplaces in parallel.

**Rejected:** same rejection as ADR-0070 Opt.B — registry, conflict
resolution and offer deduplication for unmeasured sell-through. Revisit
when Sedo measures incremental distribution.

### Option C: Sedo adapter without the correctness fixes

Ship the adapter, fix manual/mismatch/pagination later.

**Rejected:** ships two known silent-corruption paths (misrouting,
stuck-pending) on a wider marketplace surface.

## Decision Outcome

**Chosen: Option A.** Implementation:

- `src/providers/listing/sedo-listing-provider.ts` — new adapter.
- `src/providers/listing/remote-id.ts` — shared id/page-cap helpers.
- `src/providers/listing/dan-listing-provider.ts`,
  `src/providers/listing/afternic-listing-provider.ts` — adopt the
  shared helpers (no behavior change on happy paths).
- `src/providers/listing/index.ts`, `src/config.ts`,
  `src/app/composition-root.ts`, `.env.example` — wiring + docs.
- `src/listing/listing-manager.ts` — manual local path, mismatch guard,
  offer amount guard.
- `src/api/routes/listings.ts` — `LISTING_MARKETPLACE_MISMATCH` → 400.

## Consequences

### Positive

- Sedo distribution unlocked behind one env switch with graceful
  manual-mode degrade.
- No draft can publish to the wrong marketplace silently; manual publish
  no longer hits the UNIQUE constraint.
- Sync pagination is bounded on every HTTP adapter; string remote ids
  no longer produce NaN linkage.

### Negative

- `godaddy` is still label-only (same silent-label situation Sedo just
  left; publish guard now converts it to a loud 400).
- Sedo remote schema is adapter-shaped until verified against live
  credentials (same caveat as Afternic in ADR-0070).
- Truncation at the 50-page cap needs a re-run to continue (surfaced in
  sync errors; acceptable for a manual/scheduled sync).

### Compliance and Security Implications

- No new secrets beyond the existing `*_API_KEY` env pattern; keys stay
  in memory, never logged.
- Trademark-gate enforcement points unchanged (pre-insert, pre-publish,
  sync mirror).

### Migration and Monitoring Plan

- Rollout: `LISTING_PROVIDER=sedo` + `SEDO_API_KEY`; rollback to
  `manual` is env-only.
- Watch: `LISTING_MARKETPLACE_MISMATCH` 400 rate (mislabelled drafts),
  `pagination truncated` sync errors, Sedo `sync()` error strings.

### Validation

- Unit: `sedo-listing-provider.test.ts` (auth, mapping, string ids,
  pagination cap, factory wiring); `listing-manager.test.ts` (manual
  publish, mismatch, invalid amounts); `listings.test.ts` (400 mapping).
- Gate: `npm run typecheck`, `npm run lint`, listing test suites.

---

*This ADR was created following the MADR 4.0.0 standard. All DOMINUS ADRs should be consistent with the ADR series starting at `docs/adr/0001-project-architecture.md`.*