Skip to content

Nobody can propose a trade with a chosen partner #18

Description

@nicklaunches

Reported by a member on X on 2026-08-03, the morning the Tuesday digest went out:

I need help, as I see a mail stating 2 sites you could trade with 1. Marketing · DR 0 · 0 given / 0 received & 2. SEO · DR 22 · 0 given / 1 received
What can be done here, in dashboard, Its hard to navigate.

He is right, and it is not a navigation problem.

What he was told to do

The digest rendered propose_trade partnerId="<uuid>" under every candidate row, and again
as the primary call to action. propose_trade has never existed. It appeared in exactly
two places in the repo — src/emails/digest.tsx and a doc comment in
src/lib/services/mask.ts — and is registered nowhere in src/lib/mcp/tools.ts, absent
from /docs/mcp, and backed by no service function and no server action.

Matching is entirely server-initiated. The only writer of exchange_matches is
upsertMatch, whose only caller is autoPair, whose only caller is setSiteStatus on
admin approval. A member cannot start a trade with a chosen partner in either interface.

It compounds. The digest cron skips anyone with an open match
(src/app/api/cron/digest/route.ts:94-102), so every digest recipient is guaranteed to
land on the "No open matches" empty state
. The email offers partners and the dashboard is
empty by construction. That is the whole of his experience.

For the record, the data was fine — only the instruction was not. He is techfusiongear.com
(SEO, DR 21); his two candidates were veldo.app (Marketing, DR 0) and contextbolt.com
(SEO, DR 22), both real, both correctly masked.

What has been done

The advertisement is gone: the digest no longer prints a tool name or a bare uuid, and it
now states plainly that pairing is automatic and that a match arrives as its own email. The
/app empty state was corrected too — it claimed the Tuesday sweep pairs people, when the
sweep only mails. That was honesty work. The capability is still missing, which is what
this issue tracks.

What a real fix has to cover

  • proposeTrade(...) in src/lib/services/matches.ts, reusing upsertMatch and
    orderPair so the sorted-pair uniqueness invariant keeps meaning something.
  • A propose_trade MCP tool in src/lib/mcp/tools.ts with its own budget in
    src/lib/limits.ts. Budgets are keyed by tool name and shared across both interfaces on
    purpose, so the browser cannot become the cheap way in.
  • A browser surface on /app. The member who complained was in a browser, and /app has
    no browse or search section at all today. searchPartners exists but is MCP-only.
  • exchange_matches.proposedById already exists and is always null. This is what that
    column is for. No schema change should be needed.

Open questions, all product rather than technical

  • Must a proposal respect candidateCategories / MATCH_GROUPS, or may a member propose
    across any category? The auto-matcher bands deliberately; a human picking on purpose may
    not want to be banded.
  • What stops one member proposing to the entire member base? A rate limit is abuse control,
    not a cap on how many open proposals a single site may hold.
  • What happens when the partner already has an open match? The digest currently offers them
    regardless — contextbolt.com was offered to him while its own match with clksy.co sat
    in agreed. That was left as-is on purpose, but a propose flow forces the question.
  • When the proposer owns several sites, do they pick which one is on their end?

Adjacent, found while investigating

  • DigestProps.standingNote is supported by the template and by notifyDigest, but the
    cron never passes it (route.ts:157-162), so the standing footer has never rendered in
    production.
  • The cron uses only sites[0].category (route.ts:104), so a member with sites in two
    categories only ever receives digests for one of them.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestfeedbackReported by a member, from outside the repo

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions