offplan · online
Plan · stage1

Stage 1 — Wave 2 Chunk 5: §6 User Journeys

Approvedplanstage1priority P0
Ratified
2026-05-08
Created
2026-05-08
Priority
P0
Tags
ux, architecture, domain

Goal

Закрыть 4 пункта из CONV-22 What's Next по §6 User Journeys (Sales Agent ↔ Buyer flow):

  1. D — status protection (unit status flip Available→Reserved/Sold) + E — buyer↔unit linkage — unified mechanism (decision локальное в CONV-22 line 42, deep spec потерян, нужна reconstrucция)
  2. F — request-more (buyer запрашивает additional units / unit pages) — wholly new design space, deferred Open Question
  3. G — channels (Email + WhatsApp + Copy link для отправки tokenised presentation links) — wholly new

Pattern Chunks 3/4: surgical правки + 2 новые subsections в §6 + light phase callouts (1.10, 1.4) + small §4.2 Permissions matrix touch. Все изменения внутри launch-plan-stage-1.html + changelog v4.9 + workstream update.

Locked Decisions (CONV-25 interview)

D + E — Status-change form + buyer↔unit linkage (unified)

# Тема Решение
D1 Form shape (i') Augmented single-click verification form. Sales Agent в админке открывает unit card → нажимает «Mark as Reserved» / «Mark as Sold» (status implicit from button) → modal с полями. Roman'у соответствует «verification form» semantics из §1 SA definition cross-link. Frictionless когда buyer уже в системе.
D2 Required fields Email buyer'а — единственное всегда-required поле. Если email matches existing buyer-record для этого Sales Agent'а → name + phone displayed read-only (не дублируется ввод). Если email НЕ matches existing record → name + phone становятся required (создаётся новый buyer-record).
D3 Optional fields Notes (free text, в неё можно вписать deal value, conditions, комментарий) + File upload (PDF / JPG — signed booking letter, deposit receipt, etc.).
D4 Status mechanic Status выбирается кликом по кнопке («Mark as Reserved» / «Mark as Sold»), не отдельным dropdown'ом в форме. Reduces clicks, mistake-proof кнопки чётко разделены.
D5 Auto-fill после email match Из existing buyer-record: name + phone (read-only display, not editable inline), token history (для smart-match attribution). Sales Agent видит «<strong>Buyer found:</strong> Иван Петров, +971 50 123 45 67 — ваш buyer от 12 апреля».
D6 Reverse direction policy (a) Симметричная. Same permissions per <a href="#fd-access">§4.2</a> «Change unit status» row — Owner ✅, Admin ✅, SM ✅, CE —, SA ✅ (own assigned). Reverse-flip = Sold→Reserved, Sold→Available, Reserved→Available. Sales Agent fixes own mistakes без эскалации. Audit log catches abuse.
D7 Reverse-flip notification Каждый reverse-flip → audit log entry + email notification к Sales Manager'у (если SA откатывает); reverse-flip от Sales Manager'а → notification к Owner / Admin. Forward-flip не требует notification (стандартный workflow); reverse — «interesting event», visible by default.
D8 Buyer-record fate при reverse Buyer-record остаётся в DB (history preserved, attribution для commission). Unit↔buyer link воидится (audit log: «User X reverted Sold → Reserved at TIMESTAMP, voided link to Buyer Y»). Re-flip forward (Reserved→Sold) re-fills the form — если same email, auto-link recreates от existing buyer-record; никаких stale link'ов в DB.

Smart-match logic (E side)

Когда Sales Agent вводит email в форме:

  1. Lookup в buyer-records этого Sales Agent'а (org-scope) — если email matches existing buyer для этого SA → display banner «Buyer found: <name>, <phone>» + auto-link.
  2. Если match не найден в SA's buyer-records, lookup в Organisation-wide buyer-records — если найден → display warning «Buyer <name> existing, attributed to <other Sales Agent>. Continue?» (UX decision: повторно использовать запись или новый record per SA — gut feeling per CONV-22 «разные buyer-records под одним email, разная attribution» — создаём новый record для текущего SA).
  3. Если ни там, ни там → name + phone становятся required, создаётся новый buyer-record, attribution = текущий SA.
# Тема Решение
G1 Channels в scope Stage 1 Email + WhatsApp + Copy link (3 канала). SMS dropped — низкий ROI vs vendor pain (Twilio account, carrier compliance for int'l). Deferred Stage 2 если попросят.
G2 Implementation approach Light deep-link approach. Email = native через Phase 1.8 backend (SES / Mailgun, как уже в plan'е). WhatsApp = https://wa.me/<phone>?text=<encoded message> — открывает SA's own WhatsApp с предзаполненным message; SA жмёт «Send». Copy link = clipboard. Ноль vendor integrations, ноль compliance overhead.
G3 Tracking URL parameter — `?ch=email wa link` добавляется к buyer-token URL'у когда SA выбирает channel. Backend увеличивает counter «N opens per channel per project». В analytics видно «WhatsApp converts лучше для UAE buyer'ов» — для product decisions.
G4 Default message text Pre-filled на Org locale (fall-back English): «Hi <buyer_name>, here's the presentation we discussed: <link>». SA редактирует inline в admin'е перед клик'ом «Send». Отдельные templates per channel — Stage 2 (если попросят).
G5 Edge cases (a) Phone не заполнен для WhatsApp → button disabled с tooltip «Phone required for WhatsApp». (b) Multiple channels одновременно — same buyer-token URL, разный ?ch= параметр; одна buyer-record, multi-channel attribution counter. SA может send via Email + WA одновременно («не уверен какой buyer чаще проверяет»). (c) Buyer открывает link не из того канала где SA отправил (forwarded email → buyer открывает в WhatsApp web) — ?ch= parameter preserved, attribution unchanged, но channel counter показывает «opened via Email channel» по URL parameter, не по browser context. Это intentionally simple — не отслеживаем cross-channel migration.

F — Request-more (DEFERRED)

F-A: Какой scope доступа у buyer'а с валидным token'ом помимо самой ссылки в email'е — Open Question, 2 candidate variants для Roman ratification:

Решение по F-A влияет на:

Decision deferred до Roman call / next /plan iteration на §6 (post-Wave 2). В этом chunk'е F-A документирован обоими candidate'ами в Open Questions section с UX implications для каждого; mini-patch после ratification обновит §6.

Approach

Approach B (New §6.1 + §6.2 subsections):

Pattern Chunks 3/4 (CONV-25 build): anchor IDs (fd-buyer-status, fd-buyer-channels), cross-links, callout style.

Steps

A. §6 lead — light intro update

A1. Insert one paragraph после existing lead в §6 (after line 1062), кратко flagging что §6.1 / §6.2 ниже describe Sales Agent ↔ Buyer mechanics in detail.

<p style="margin-bottom:14px; font-size:13.5px; color:var(--text-mid);"><strong>Внутри §6:</strong> существующие <code>&lt;details&gt;</code> блоки покрывают buyer entity model и token mechanics. Подробные subsections ниже: <a href="#fd-buyer-status" style="color:var(--text-mid);">§6.1 — Sales Agent status-change form (D+E unified mechanism)</a> + <a href="#fd-buyer-channels" style="color:var(--text-mid);">§6.2 — Channels (Email + WhatsApp + Copy link)</a>.</p>

B. New §6.1 — Sales Agent status-change form (D+E unified)

B1. Insert new subsection after existing 3rd <details> («Forward email / shared token») on line 1090, before closing </div> на 1091.

<h4 id="fd-buyer-status" style="color:var(--navy); margin:24px 0 10px;">6.1 — Sales Agent status-change form (D + E unified mechanism)</h4>
<p style="margin-bottom:10px;"><strong>Use case:</strong> Sales Agent проводит встречу с buyer'ом, договорился о reservation / sold deal'е, нужно поставить unit в Reserved / Sold + связать buyer'а с unit'ом для attribution / commission. Decision (CONV-22): D (status protection) и E (buyer↔unit linkage) <strong>объединены в один mechanism</strong> — заполнение status-change form auto-links buyer-record by email lookup (smart-match). Альтернатива (2 separate UI: status form + manual unit-buyer link в карточке) отвергнута как излишняя ручная работа.</p>

<div style="margin:12px 0; padding:14px 18px; background:var(--sand-50); border-left:3px solid var(--gold); border-radius:0 6px 6px 0;">
  <strong style="color:var(--navy); font-size:14px;">Form shape (augmented single-click verification)</strong>
  <ul style="margin:8px 0 0 20px; font-size:13.5px; color:var(--text-mid);">
    <li><strong>Trigger:</strong> Sales Agent в админке Project → unit card → нажимает кнопку «Mark as Reserved» (амбер) или «Mark as Sold» (red). Status выбирается кликом, не dropdown'ом — reduces clicks, кнопки чётко разделены.</li>
    <li><strong>Required field:</strong> <code>buyer email</code> (с autocomplete'ом из buyer-records этого Sales Agent'а — recent first).</li>
    <li><strong>Conditional required:</strong> если email НЕ matches existing buyer-record → <code>buyer name</code> + <code>phone</code> становятся required (создаётся новый record). Если email matches existing record → name + phone displayed read-only (auto-fill из existing).</li>
    <li><strong>Optional:</strong> <code>notes</code> (free text — deal value, conditions, comment) + <code>file upload</code> (PDF / JPG: signed booking letter, deposit receipt, photo подписанного контракта, etc.).</li>
    <li><strong>Submit:</strong> server validates → создаёт/links buyer-record → flips unit status → audit log entry → email notification (см. ниже).</li>
  </ul>
</div>

<div style="margin:12px 0; padding:14px 18px; background:var(--sand-50); border-left:3px solid var(--gold); border-radius:0 6px 6px 0;">
  <strong style="color:var(--navy); font-size:14px;">Smart-match logic (email lookup, E side)</strong>
  <ol style="margin:8px 0 0 20px; font-size:13.5px; color:var(--text-mid);">
    <li><strong>Lookup в buyer-records этого Sales Agent'а</strong> (org-scope, recent first) — если email match → display banner «<em>Buyer found: <strong>Иван Петров</strong>, +971 50 123 45 67 — ваш buyer от 12 апреля</em>» + auto-link к существующему record'у.</li>
    <li><strong>Lookup в Organisation-wide buyer-records</strong> (если в (1) match не найден) — если match → display warning «<em>Buyer <strong>Иван Петров</strong> existing, attributed to <strong>Алексей Иванов</strong>. New buyer-record will be created для вашей attribution. Continue?</em>». Решение per CONV-22: разные buyer-records под одним email = разная attribution; новый record создаётся для текущего SA.</li>
    <li><strong>Если match не найден ни там, ни там</strong> → form требует name + phone, создаётся new buyer-record (attribution = текущий SA), unit↔buyer link создаётся.</li>
  </ol>
</div>

<div style="margin:12px 0; padding:14px 18px; background:#fff; border:1px solid var(--border); border-radius:6px;">
  <strong style="color:var(--navy); font-size:14px;">Triggers + reverse direction policy</strong>
  <ul style="margin:8px 0 0 20px; font-size:13.5px; color:var(--text-mid);">
    <li><strong>Forward triggers:</strong> Available → Reserved · Available → Sold · Reserved → Sold (с pre-fill «same buyer?» — если в Reserved уже есть linked buyer-record, форма pre-fill'ит email).</li>
    <li><strong>Reverse triggers:</strong> Sold → Reserved · Sold → Available · Reserved → Available. Симметричная policy — same permissions per <a href="#fd-access">§4.2 «Change unit status»</a> row (Owner / Admin / SM / SA own-assigned). Sales Agent fixes own mistakes без эскалации к manager'у. Audit log + email notification catches abuse.</li>
    <li><strong>Email notification на reverse-flip:</strong> к Sales Manager'у (если SA откатывает); к Admin / Owner (если SM откатывает). Forward-flip — без notification (standard workflow); reverse — «interesting event» worth surfacing.</li>
    <li><strong>Buyer-record fate при reverse:</strong> buyer-record <strong>остаётся в DB</strong> (history + attribution preserved для commission). Unit↔buyer link <strong>воидится</strong> (audit log entry: «<em>User X reverted Sold → Reserved at TIMESTAMP, voided link to Buyer Y</em>»). Re-flip forward (Reserved → Sold снова) — SA re-fills форму; если same email — auto-link recreates от existing buyer-record. Никаких stale link'ов в DB.</li>
  </ul>
</div>

<div style="margin:12px 0; padding:14px 18px; background:#fff; border:1px solid var(--border); border-radius:6px;">
  <strong style="color:var(--navy); font-size:14px;">Audit log</strong>
  <ul style="margin:8px 0 0 20px; font-size:13.5px; color:var(--text-mid);">
    <li><strong>Forward-flip:</strong> «<em>User X marked unit Y as Reserved/Sold; linked to Buyer Z (existing | new) at TIMESTAMP. Notes: «...». File: foo.pdf (если приложен)</em>».</li>
    <li><strong>Reverse-flip:</strong> «<em>User X reverted unit Y from Sold → Reserved at TIMESTAMP. Voided link to Buyer Z. Reason: «...» (notes если заполнено)</em>».</li>
    <li><strong>Visible to:</strong> Owner / Admin / Sales Manager (full audit trail per Project в Project Settings → Activity log). Sales Agent — own actions only (own assigned units history).</li>
    <li><strong>Schema-level дизайн</strong> — Phase 1.3 implementation level (consistent с pattern <a href="#fd-access">§4.6 View-as-Agent</a> + <a href="#fd-visibility-pin">§5.1 PIN-protected</a> audit).</li>
  </ul>
</div>

<p style="font-size:13px; color:var(--text-mid); margin-top:14px;"><strong>Cross-link:</strong> <a href="#phase-1-10" style="color:var(--text-mid);">Phase 1.10</a> — verification form UI build + WhatsApp button (см. §6.2) · <a href="#phase-1-4" style="color:var(--text-mid);">Phase 1.4</a> — operator dashboard sees flip audit + reverse-flip emails · <a href="#fd-access">§4.2</a> — permission row для status changes (forward + reverse symmetric).</p>

C1. Insert after §6.1 (after Step B1 closing </p>).

<h4 id="fd-buyer-channels" style="color:var(--navy); margin:24px 0 10px;">6.2 — Channels — Email + WhatsApp + Copy link</h4>
<p style="margin-bottom:10px;"><strong>Use case:</strong> Sales Agent после встречи / звонка отправляет buyer'у tokenised presentation link через подходящий канал. UAE / MENA reality — WhatsApp dominant (~80% delivery), Email — деловой / formal, SMS — устаревает. Stage 1 covers 3 channels через <strong>light deep-link approach</strong> (zero vendor integrations).</p>

<div style="margin:12px 0; padding:14px 18px; background:var(--sand-50); border-left:3px solid var(--gold); border-radius:0 6px 6px 0;">
  <strong style="color:var(--navy); font-size:14px;">3 channels (Stage 1)</strong>
  <table style="margin:8px 0 0; font-size:13px;">
    <thead><tr><th style="width:140px;">Channel</th><th>Mechanic</th><th>Tracking</th></tr></thead>
    <tbody>
      <tr><td><strong>Email</strong></td><td>Native send из platform'ы через Phase 1.8 infra (SES / Mailgun). Default sender = Organisation's verified email; subject = «<em>Presentation: <Project name></em>»; body = composed message + tokenised links. Phase 1.8 handles delivery, bounces, opens.</td><td>URL: <code>?ch=email</code>. Opens counted в analytics.</td></tr>
      <tr><td><strong>WhatsApp</strong></td><td>Deep-link to SA's own WhatsApp client: <code>https://wa.me/&lt;phone&gt;?text=&lt;encoded message&gt;</code>. SA жмёт кнопку «Send via WhatsApp» в admin'е → browser открывает <code>web.whatsapp.com</code> (или native app если на mobile) с предзаполненным message; SA жмёт «Send» уже там. <strong>Никаких vendor integrations</strong> — никакого WhatsApp Business API, никакой compliance approval, никаких monthly fees.</td><td>URL: <code>?ch=wa</code>. Opens counted.</td></tr>
      <tr><td><strong>Copy link</strong></td><td>Кнопка «Copy buyer link» в admin'е → clipboard'у копируется tokenised URL с <code>?ch=link</code>. SA pastes куда хочет — Telegram / iMessage / LinkedIn DM / SMS / etc. Универсальный fallback.</td><td>URL: <code>?ch=link</code>. Opens counted (без знания target channel).</td></tr>
    </tbody>
  </table>
</div>

<div style="margin:12px 0; padding:14px 18px; background:#fff; border:1px solid var(--border); border-radius:6px;">
  <strong style="color:var(--navy); font-size:14px;">UI flow в Sales Agent admin'е</strong>
  <ol style="margin:8px 0 0 20px; font-size:13.5px; color:var(--text-mid);">
    <li>SA в Project admin'е → buyer-record card (или после Object Builder при создании презентации) → «<strong>Send to buyer</strong>» button.</li>
    <li>Modal: <em>compose message</em> (default text editable: «Hi <buyer_name>, here's the presentation we discussed: <link>») + selected units (chips, removable) + 3 channel buttons:
      <ul style="margin:4px 0 0 16px;">
        <li><strong>📧 Send via Email</strong> — disabled if buyer email is empty.</li>
        <li><strong>💬 Send via WhatsApp</strong> — disabled if buyer phone is empty (tooltip «Phone required for WhatsApp»).</li>
        <li><strong>🔗 Copy link</strong> — always enabled.</li>
      </ul>
    </li>
    <li>Click → action: Email отправляется с backend'а; WhatsApp / Copy открывают browser handlers.</li>
    <li><strong>Multi-channel в одну операцию</strong> — SA может click Email AND WhatsApp одновременно («не уверен какой канал buyer чаще проверяет»). Same buyer-token URL, разный <code>?ch=</code> param на каждой версии — multi-channel attribution counter в analytics.</li>
  </ol>
</div>

<div style="margin:12px 0; padding:14px 18px; background:#fff; border:1px solid var(--border); border-radius:6px;">
  <strong style="color:var(--navy); font-size:14px;">Default message text + locale</strong>
  <ul style="margin:8px 0 0 20px; font-size:13.5px; color:var(--text-mid);">
    <li>Pre-filled на Org locale (Stage 1 supports English + Russian per ADR 0007 i18n; Arabic Stage 2 если рынок попросит).</li>
    <li>Template (English): <em>«Hi <buyer_name>, here's the presentation we discussed: <link>»</em>. Russian: <em>«Привет <buyer_name>, вот презентация о которой мы говорили: <link>»</em>.</li>
    <li>SA редактирует inline в admin'е перед клик'ом «Send» — full freedom для personalisation.</li>
    <li><strong>Per-channel templates</strong> (different text для Email vs WhatsApp) — Stage 2 если попросят. В Stage 1 — same text для всех channels (SA может вручную подкорректировать перед каждым send'ом).</li>
  </ul>
</div>

<p style="font-size:13px; color:var(--text-mid); margin-top:14px;"><strong>Cross-link:</strong> <a href="#phase-1-10" style="color:var(--text-mid);">Phase 1.10</a> — UI flow build + 3 channel buttons · <a href="#phase-1-8" style="color:var(--text-mid);">Phase 1.8</a> — Email infra (already in plan) · <a href="#phase-1-4" style="color:var(--text-mid);">Phase 1.4</a> — operator dashboard channel analytics aggregate.</p>

<p style="font-size:13px; color:var(--text-mid); margin-top:8px;"><strong>Stage 2 channels backlog:</strong> SMS via Twilio (carrier compliance, monthly fee), centralised WhatsApp via Business API (от platform'ы send not from SA's phone — pretty UX, audit log, opt-in flows; vendor onboarding required).</p>

D. §4.2 Permissions matrix — sub-bullet про reverse-flip notification

D1. Update existing «Change unit status» row (line ~882) — same row label / permissions, но добавить short note под таблицей про reverse-flip email notification.

Альтернатива (если row note inline получается ugly) — add as a paragraph after matrix table ends (post line ~887).

<p style="font-size:13px; color:var(--text-mid); margin-top:8px;"><strong>Reverse-flip note (CONV-25):</strong> reverse направление status change (Sold → Reserved/Available, Reserved → Available) — same permissions как forward (Owner / Admin / SM / SA own assigned). Каждый reverse-flip → audit log + email notification к одному уровню выше (SA → SM, SM → Admin/Owner). Forward-flip — audit only, без notification (standard workflow). См. <a href="#fd-buyer-status">§6.1 — reverse direction policy</a>.</p>

E1. Update existing line 619 — change cross-link from #phase-1-10 to #fd-buyer-status (более precise — SA verification form lives в §6.1, не в Phase 1.10 implementation level).

<tr><td><strong>Sales Agent</strong></td><td>Buyer / presentation flow. Меняет status assigned units через verification form (см. <a href="#fd-buyer-status" style="color:var(--navy);">§6.1</a>). Не редактирует контент. Делится на <strong>Internal</strong> (Client из команды самой Organisation) и <strong>External</strong> (Client из другой Organisation, приглашён через guest-organisation invite) — operational различия в <a href="#fd-access" style="color:var(--navy);">§4.5</a>.</td></tr>

F. Phase 1.10 callout — verification form UI + channel buttons

F1. Add new bullet в существующий v4.7 / v4.8 callout (где живут recent CONV-25 entries):

<li><em>v4.9 (CONV-25):</em> <strong>Status-change verification form UI (D+E unified) + 3-channel buyer-link send.</strong> Form: button-driven status select + email-required field with smart-match autocomplete + auto-fill from existing buyer-record + optional notes/file upload. Reverse-direction symmetric per <a href="#fd-access">§4.2</a> with email notification одну ступень выше. Buyer-link send modal: 3 channel buttons (Email native via Phase 1.8 / WhatsApp via <code>https://wa.me/...</code> deep-link / Copy link clipboard) + <code>?ch=</code> URL parameter tracking. F-A (buyer browse scope from token) — deferred Open Question, two candidate variants documented в <code>plans/stage1-chunk-5-journeys.md</code>. См. <a href="#fd-buyer-status">§6.1</a> + <a href="#fd-buyer-channels">§6.2</a>.</li>

G. Phase 1.4 callout — operator dashboard sees status flip audit

G1. Add new bullet в существующий v4.7 / v4.8 callout:

<li><em>v4.9 (CONV-25):</em> <strong>Status-flip audit visibility + reverse-flip email pipeline.</strong> Operator dashboard читает audit log entries для status flips (forward + reverse) per Organisation — для support troubleshooting (e.g., «client claims refund but unit Sold» → operator смотрит timeline). Reverse-flip emails (SA → SM, SM → Admin/Owner) идут через Phase 1.8 transactional email infra; operator video log this как separate event class «status-reverse-notification-sent». Anti-abuse aggregate: «Organisation X had >20 reverse-flips в 30 дней» highlighted в overview (фрод-индикатор: фейковые reservations и тут же откаты для commission games). Channel analytics aggregate (Email / WhatsApp / Copy opens по проектам) — standalone widget в operator overview. См. <a href="#fd-buyer-status">§6.1 audit log</a> + <a href="#fd-buyer-channels">§6.2 tracking</a>.</li>

H. Changelog v4.9 entry

H1. Append v4.9 entry в launch-plan-changelog.html поверх v4.8.

I. Workstream update

I1. workstreams/stage1-roman-integration.md:

J. Preview repo sync

J1. Sync 2 файла в ~/code/offplan-online/preview/plan/: launch-plan-stage-1.html + launch-plan-changelog.html. Commit + push.

Files

Dependencies

Testing

Risks

  1. §6.1 размер — может вырасти больше 200 lines если все 4 boxes (form / smart-match / triggers / audit) детально расписаны. Mitigate: использовать compact bullet style, не embedded sub-tables (кроме triggers row table). Если в /build выходит >250 lines — split на §6.1 (form + smart-match) и §6.2 промотать в §6.3 (channels), переnumber'я.
  2. F-A deferred — может понадобиться mini-patch после Roman ratification. Mitigate: Open Question section детально документирует обе variants (UX implications, Phase 1.10 routing impact). После ratification — short patch в §6 (1 paragraph + Phase 1.10 callout update). Не block'ит Chunk 5.
  3. Smart-match logic ambiguity — gut feeling per CONV-22 «разные buyer-records под одним email = разная attribution» применён, но Roman может не подтвердить если он имел в виду другое. Mitigate: spec'ed как assumption; Open Question fallback в plan.
  4. Reverse-flip notification может перетянуть в Phase 1.8 (Email infra) — schema schema design каких events emails отправлять, какие шаблоны, opt-out controls. Mitigate: упомянуть как requirement без deep dive в Phase 1.8 spec — это уровень build implementation.
  5. WhatsApp deep-link — phone format edge cases. International phones (+971...) versus locale-specific. wa.me принимает E.164 без + (e.g., 971501234567). Mitigate: phone normalization at validate-time в form input (Stage 1 = simple regex; libphonenumber Stage 2 если ошибки).
  6. Channel tracking может drift?ch= parameter может потеряться если buyer forwards link (некоторые messengers strip query strings). Mitigate: document как «known limitation» в §6.2 — channel analytics показывает «opens by intended channel», не «opens by actual channel». OK для MVP.

Workstreams

Evaluation

Done when:

Definition of NOT done (deferred):

Open Questions

F-A — Buyer browse scope from valid token (deferred for Roman ratification)

Context. Currently §6 line 1071 says «buyer возвращается к другим ссылкам в email'е или гуляет по сайту с сохранённой attribution» — implies project-level browsing OK, но уровень доступа не специфицирован. Если project на Discovery preset (default — anonymous не видит цены), buyer открывает unit-X по token'у, нажимает «Back to Project» → попадает на main project page → что показано там? Два candidate variants:

Variant (i) — Token = elevated visitor для всего project'а. Buyer гуляет по проекту с эффективным Full sales preset (видит все units, цены, availability badges, рендеры, 360° tours) — независимо от Public Visibility setting. Hearts ANY unit → notification к Sales Agent'у. Mental model: «SA привёл buyer'а в офис показывать проект — buyer видит всё». Self-serve discovery — buyer находит дополнительные интересные units и сам hearts'ит без необходимости звонить SA. Explicit «Request more options» CTA становится опциональным (small «Show me similar» on unit-page → notify SA).

Variant (iii) — Token = elevated до Discovery preset, но не Full sales. Buyer видит project с renders + 360° + floorplate + counter «N units», но без цен и individual unit pages — только token'd unit pages открыты в Full sales (т.е. buyer видит цены только на тех units которые SA explicitly link'нул в email). Owner'у даётся Stage 1 toggle «Allow buyer-token full project access» (per-project) который elevates до Variant (i). Default = mid-conservative, opt-in elevation. Plus: explicit «Request more options» CTA на unit-page обязательна (notify SA → SA sends new email с new tokenised links).

Decision criteria:

  1. UAE / MENA off-plan property sales reality — how much price visibility control do studios actually want? If "Sales Agent must show prices personally" is real norm → (iii). If "buyer expects self-serve browsing once authed" is closer → (i).
  2. Roman call sentiment — он упомянул что buyer не должен «вертеть носом» через open project but не сказал точно где cutoff. Need explicit ask.
  3. Implementation cost — (i) simpler по routing; (iii) добавляет toggle UI + per-page filter logic в Phase 1.10. (i) ships faster.

Resolution path: