cocottetech/@platform/codebase/@features/ai-copilot/docs/N-provider-coop.brief.md
natalie 1b719e1fd7 chore(bootstrap): initial V4 commit
Clean successor to V3 (forge: lilith/atlilith). Seeded from local Mac
working tree at ~/Code/@projects/@cocottetech/. node_modules and build
artifacts excluded via .gitignore.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 08:11:41 -07:00

18 KiB
Raw Blame History

N — Provider coop (inter-org intel sharing)

Goal

v2's client-intel feature is a provider-coop safety-intel network: providers publish reports on clients (rating, safety flags, would-work-again) with COOP_ONLY visibility so other coop members can see the warning before they take a booking. This is structurally different from per-tenant prospect data (lives in this user's / org's database) — it's inter-tenant sharing with consent, attribution choice, and abuse-reporting on the coop itself.

Brief J ports the data layer. This brief designs the UX of being in a coop: opting in, publishing an intel report, reading peers' reports, scoping visibility, staying anonymous (or not), and flagging abuse of the coop.

Designer skim

  • Headline UX: Provider-side safety-intel network. Opt-in coops (regional / agency / vertical). Reports attributed by default, optionally anonymous (lower weight). Plain-register voice throughout — coop misreads are expensive.
  • Sections (8): N1 data model · N2 onboarding (opt-in) · N3 reading peers · N4 publishing a report · N5 anonymity + attribution · N6 dispute / counter-report · N7 PII hashing + attachment encryption · N8 coop-side moderation.
  • Blocking Qs: OPEN-DECISIONS.md → N-Q1 coop discovery, N-Q2 reporter retaliation PII-scan.

In-the-wild copy

Coop intel surfaced before booking (plain — V5 coop rule, never warm):

Two coop reports on this number. One UNSAFE in Berlin coop, Apr 2024. One BOUNDARY_VIOLATION in Industry-TG coop, Oct 2024. Open before replying.

Anonymous-report submit warning (plain):

This report posts anonymous. Peers see "anonymous member." The Berlin moderator still sees you. Continue or attribute.

PII scan before publish (plain):

The notes mention an email Cocotte didn't hash. Want her to redact, or send as-is?

Dispute received (plain):

A subject disputed your Apr 2024 report. Moderator review pending. Your report stays visible.

Constraints

  • The coop is a trust network among providers. It is NOT a public review site. Coop intel never reaches clients, never indexes on search engines, and never appears in public CocotteAI surfaces (Brief G web companion, Brief E cross-platform variants).
  • Coop membership is opt-in. The default state for any new user/org is not in any coop. Joining is an explicit action with informed consent (per Brief D onboarding doesn't auto-enroll).
  • Reports are attributed by default, optionally anonymous. Anonymous reports carry less weight in aggregated scoring (per v2's safety-score.service.ts port).
  • The coop is multi-tenant within the platform. Coops are scoped — e.g. a regional coop (Berlin providers), an agency coop (Demimonde members), an industry-vertical coop (TG escorts). One user can be in N coops; each report carries its scope.
  • Abuse on the coop is a first-class problem. A coop's value collapses if it's used to settle personal beefs or as a competitive-suppression tool. This brief MUST cover dispute / counter-report / coop-moderation UX.
  • Reports surface in chat (Brief A), specialist drawers (Brief B + Brief L's triage specialist), and the engagement portal (Brief G). They never surface in the prospect's own surfaces (Brief K's blocklist is per-user; coop intel is per-coop).
  • Per voice.brief.md, coop intel surfaces use the plain register — no metaphor. The cost of misread coop data is too high for warmth.

N1 — Inputs / data model

Ported from v2's client-intel/backend-api/src/entities/intel-report.entity.ts, generalized for v4 multi-tenancy:

type CoopReport = {
  id: string;
  coop_id: string;                  // which coop this lives in
  reporter_user_id: string;         // attributed; required even for anonymous reports
  reporter_org_id: string | null;
  anonymous: boolean;                // if true, only the coop moderator sees reporter; peers see "anonymous member"
  subject_kind: 'prospect' | 'client_pii_hash' | 'phone' | 'email' | 'directory_handle';
  subject_value: string;             // hashed where appropriate (PII)
  rating: 1 | 2 | 3 | 4 | 5 | null;  // 1=avoid, 5=excellent; nullable for flag-only reports
  safety_flags: SafetyFlag[];        // UNSAFE | AGGRESSIVE | BOUNDARY_VIOLATION | NON_PAY | TIME_WASTER | SCAM | etc.
  would_work_again: boolean | null;
  notes: string;                     // structured: incident summary, evidence references
  attachments: AttachmentRef[];      // optional screenshots / receipts; auto-encrypted
  created_at: string;
  updated_at: string;
  withdrawn_at: string | null;       // soft-delete; visible to moderators only after withdrawal
};

type Coop = {
  id: string;
  name: string;                      // e.g. "Berlin escort coop", "Demimonde internal", "TG providers EU"
  kind: 'regional' | 'agency' | 'vertical' | 'private';
  scope: { city?: string; country?: string; vertical?: string; org_id?: string };
  member_count: number;
  moderators: string[];              // user_ids
  joined_at: string | null;          // null if not a member, ISO string if joined
  status: 'invited' | 'pending' | 'member' | 'suspended' | 'left';
  config: {
    anonymous_reports_allowed: boolean;
    min_membership_age_to_report_days: number;  // anti-abuse: must be in coop N days before reporting
    require_evidence_for_safety_flags: boolean;
  };
};

GET /api/v1/coops?user_id=... → list of coops user is in / invited to. GET /api/v1/coops/:coop_id/reports?subject=... → reports on a specific subject in this coop. POST /api/v1/coops/:coop_id/reports → publish a new report. POST /api/v1/coops/:coop_id/reports/:id/dispute → flag a report as inaccurate / abusive.

N2 — Coop membership states (Quinn's perspective)

N2a — Not a member of any coop (default)

  • No coop UI surfaces visible.
  • A discoverable, dismissable card in the chat-home empty-state inviting Quinn to explore coops she might join — small, non-pushy.
  • An "invitations" badge on the settings overlay if any coop has invited her.

N2b — Invited

  • Push notification (medium-stakes, plain-register): "Berlin escort coop invited you. 12 members. Tap to read about it."
  • Invitation detail sheet: coop name, scope, member count, moderator list, current rules (config), sample of recent activity (counts only, no contents), accept / decline / "request more info from moderator".

N2c — Pending (Quinn applied, moderator hasn't accepted)

  • Banner in coop drawer: "Waiting for moderator approval. Joined queue 2 days ago."
  • Quinn can withdraw application without explanation.

N2d — Member

  • Coop drawer (see N3) is fully active.
  • Reports start surfacing in chat + specialist drawers per N4 routing rules.

N2e — Suspended

  • Visible state in coop drawer with reason (from moderator). Quinn can read reports but cannot publish new ones until reinstated.
  • This is the moderator's primary anti-abuse tool — softer than removal.

N2f — Left

  • Coop is hidden from active surfaces but stays in audit history (Brief I append-only).
  • Reports Quinn published while a member stay published (unless she explicitly withdraws each — separate per-report action).

N3 — Coop drawer (peer to Brief B drawers)

A new top-level drawer in the chat surface, accessible from chat-home's top-bar overflow menu and via voice ("show me coop").

Drawer sections (vertically stacked, scrollable):

N3a — Coop selector header

  • Horizontal pill list of coops Quinn is in. Tap to switch active coop view.
  • "+ invitations (N)" pill if pending invites.
  • Settings cog routes to coop-config sheet (per-coop preferences).

N3b — Recent activity

  • Last 20 reports in this coop (chronological, newest first).
  • Each row: subject (handle / hashed PII display name), rating chip, safety flag chips, attribution ("you", "anonymous member", or named peer).
  • Tap row → report detail sheet.

N3c — Subjects I've reported on

  • List of subjects Quinn has published reports about, with status (active, edited, withdrawn).
  • One-tap to edit Quinn's own report on a subject.

N3d — Watchlist (subjects flagged by peers Quinn wants to track)

  • Subjects in this coop with ≥1 high-severity safety flag.
  • Each row: subject + aggregate severity + "first reported" timestamp.
  • Tap → all reports on that subject + a "first to reach out wins" race indicator if multiple coop members have draft replies to this subject in their own queues (privacy-preserving: shows count, not who).

N3e — Coop info

  • Coop name, scope, member count, moderator list, rules (read-only display of config).
  • "Leave coop" affordance (with double-confirmation).
  • "Report a moderator" affordance (escalates to platform-level moderation, not coop-level — see N6).

N4 — How coop reports surface elsewhere

Coop intel is routed through specialists, not displayed raw in main chat. Specialists per Brief L make decisions informed by coop data, but Quinn only sees the decision + a tap-to-see-coop-evidence affordance.

N4a — triage specialist (the heaviest consumer)

  • When a new inbound arrives, triage queries coops for any reports on the sender's PII/handle.
  • If a report exists with UNSAFE/AGGRESSIVE/BOUNDARY_VIOLATION/SCAM/NON_PAY flag: triage routes to draft-only with warning. Quinn sees the eligibility decision + a coop-warning chip on the engagement card.
  • Tap chip → coop report detail sheet (with reporter attribution per N1).

N4b — prospect-resolver (P4)

  • Merges coop intel into the prospect's profile in the engagement portal (Brief G).
  • Prospect card shows safety summary across coops (count of reports, aggregate severity).

N4c — bookings-{directory} specialists

  • Pre-booking gate: if a prospective client matches a high-severity coop report, the directory specialist halts the auto-action and surfaces a high-stakes interrupt per Brief M §M3.

N4d — ai-copilot daily digest

  • A short coop section in the digest: "3 new reports in Berlin coop (1 high-severity match against one of your current threads — see triage)."

N5 — Publishing a report (the most carefully designed flow)

The act of publishing intel is deliberate, friction-deliberate, and reversible.

N5a — Entry points

  • From engagement card overflow → "Report to coop"
  • From prospect detail drawer (Brief B3) → "Coop intel" tab → "Add report"
  • From chat — Quinn says "report this client to the Berlin coop" → ai-copilot drafts a structured report and shows it for review

N5b — Compose sheet

  • Subject selector — auto-fills from context if entry was from a card/drawer; else manual subject entry with PII-hash on the fly (Quinn types phone → app hashes locally before sending).
  • Coop selector — defaults to Quinn's most-active coop; multi-select allowed (one report, multiple coops, with per-coop visibility).
  • Rating slider — 15, with descriptive labels at each notch.
  • Safety flags — multi-select chips (UNSAFE / AGGRESSIVE / BOUNDARY_VIOLATION / NON_PAY / TIME_WASTER / SCAM / OTHER).
  • Notes — required text field, character-counted, plain-register prompt copy: "What happened? Be specific. Other providers will rely on this."
  • Attachments (optional) — screenshots, receipts; auto-encrypted client-side before upload (per N7 privacy).
  • Attribution toggle — "Publish as you" (default) vs "Publish anonymously" (with disclaimer: "Moderators can still see your identity. Anonymous reports carry less weight in aggregate scoring.").
  • Preview — full report as it'll appear to peers, plain-register render.

N5c — Confirmation

  • High-stakes interrupt per Brief M: "Publishing to Berlin coop. 12 members will see this within minutes. Subject can never see it. You can edit or withdraw it later. Publish?"
  • Two buttons: "Publish" (primary, NOT marked safe) + "Hold off" (secondary).

N5d — Post-publish

  • Confirmation in chat (plain register): "Published. Subject hash: abc123. 12 peers notified."
  • A row enters Quinn's audit drawer (Brief I) — coop reports are first-class agent_actions rows.
  • The report appears in the coop drawer's recent-activity feed within 30s.

N6 — Disputes and abuse on the coop

The coop's existential risk is misuse — competitive suppression, personal vendetta, false-flag reports.

N6a — Disputing a report

  • Anyone in the coop can file a dispute on any report, including their own.
  • Dispute sheet: structured reason (inaccurate, personal_dispute, competitive_suppression, revenge, pii_violation, other) + free-text context.
  • Disputed reports gain a "disputed" badge in the coop drawer + suppress their score weighting until moderator review.

N6b — Moderator review queue

  • Coop moderators see disputed reports in a moderator-only drawer section.
  • Moderator actions: uphold report (dispute dismissed), withdraw report (soft-delete), suspend reporter (per N2e), or escalate to platform-level moderation (per N6c).

N6c — Platform-level coop moderation

  • A specialty role (NOT a per-coop moderator) that handles cross-coop abuse: a member abusing multiple coops, or a moderator being captured by a faction.
  • UX surface: a "report a moderator" affordance in N3e + a platform-admin dashboard (out of scope for this brief; engineering brief).

N6d — Coop dissolution

  • If a coop is deemed compromised, platform-level moderation can dissolve it — members archive their published reports, coop is hidden from new joins, history preserved in audit.

N7 — Privacy mechanics

N7a — PII hashing

  • Phone numbers, emails, handles are hashed client-side before upload using a per-coop salt (so the same phone reported in two coops has different hashes — no cross-coop joining).
  • Subject values are never stored in plaintext on platform.db.

N7b — Attachment encryption

  • Attachments (screenshots, receipts) are encrypted client-side with per-coop keys; decryption happens client-side when viewing. Platform.api stores ciphertext.

N7c — Data retention

  • Withdrawn reports: hidden from peers but kept in moderator-visible archive for 90 days, then hard-deleted.
  • Left-coop reports: published reports stay published unless individually withdrawn (per N2f). Quinn's account departure does NOT mass-withdraw.

N7d — Audit trail

  • Every coop-report event (publish, edit, withdraw, dispute) is an agent_actions row (Brief I). Quinn can see her full coop history in audit.

States to design

  • N2aN2f membership states (each has a distinct visual + affordance set).
  • Coop drawer sections N3aN3e.
  • Compose sheet (N5b) — empty, mid-fill, validation errors, attachment uploading, preview.
  • Publish confirmation (N5c) — the high-stakes interrupt.
  • Report detail sheet — attributed peer report, anonymous peer report, your own report (with edit/withdraw affordances).
  • Coop warning chip on engagement cards (N4a).
  • Daily digest coop section (N4d).
  • Dispute filing sheet (N6a).
  • Moderator review queue (N6b) — coop-moderator role variant of the coop drawer.
  • Coop dissolution interrupt (N6d) — informational, no Quinn action required.

Out of scope

  • The economics of coop membership (free? paid? per-coop dues?) — product-pricing question.
  • Cross-coop intelligence aggregation algorithms (safety-score.service.ts port-as-is initially; future ML refinements are P5+).
  • Public-facing or law-enforcement disclosure surfaces — explicitly NOT a feature; coop intel is provider-coop-only.
  • Auto-publish via AI — coops are an explicit deliberate Quinn action; no specialist auto-publishes a report. (Specialists can DRAFT a report for Quinn's approval, but never publish autonomously.)

Open questions

  • Coop discovery: how does Quinn find coops she's eligible to join? Lean: invitations + platform-curated suggestions based on her scope (city, vertical, org). Avoid public coop catalog (privacy + abuse risk).
  • Score weighting of anonymous reports: N1 says anonymous carries less weight; how much less? 50%? 25%? Engineering / data-modeling decision.
  • PII hashing salt rotation: per-coop salt is great for privacy but breaks longitudinal joins if the salt changes. Lean: never rotate per-coop salts unless the coop is compromised.
  • Cross-platform coop view (Brief E iPad / web companion): coop drawer ports to iPad/web, but report composition is iPhone-first (deliberation + privacy). Lean: peer viewing on all surfaces; composing only on phone for P0.
  • Coop-to-coop attestation: a member trusted in coop A wanting to share a report from there with coop B — explicit re-publish action with attribution chain? Future work; defer.
  • Reporter protection from retaliation: if a subject identifies themselves in the report's notes accidentally, the platform should warn before publish. Lean: PII-scan the notes field client-side before submit.
  • Brief J — port verdict for v2 client-intel (PORT); this brief is the UX consumer.
  • Brief L §L3c (triage) + §L3h (prospect-resolver) — the specialists that consume coop intel.
  • Brief I — every coop-report action is an agent_actions row; coop activity is auditable.
  • Brief K — Quinn's personal blocklist is per-user; this brief is the coop equivalent. They're complementary: a blocked sender per Brief K doesn't reach Quinn; a coop-flagged sender per Brief N reaches Quinn but with a warning.
  • Brief M §M3 — publish confirmation uses the high-stakes interrupt pattern.
  • voice.brief.md §V2c — coop UI uses plain register; metaphor would be inappropriate for safety-intel.
  • jobs-to-metaphor memory — coop intel is the rare CocotteAI surface where the cooking metaphor is OFF entirely.