_upsert_contact_from_fundraising now reads contact.phone and writes contacts.phone on
both the insert and update paths, so a phone captured from a business card persists on
the canonical contact record. Phone stays contact-level (not a grid pill field),
matching how the team edits it. Validated by test_grid_add_investor.py.
This is the SERVER half of business-card phone capture, staged for the next s9pk
(version bump + build + install). The bot's phone extraction/card/payload lands in the
same deploy, so phone never shows on a card before the box can store it. NOT yet
built or installed to the box.
The mobile "New investor" sheet now captures three optional fields beyond name/contact/note,
matching the dc (GridApp.dc.html:737):
- Initial pipeline stage — a .stage-pick chip picker, defaulting to "Not in pipeline" so a
plain directory add never auto-creates an opportunity row (Grant's call).
- A framed "Flag as Priority" toggle.
- An optional reminder (title + a progressive due-date field).
submitCreate orchestrates one-row calls in order: create (log-communication
create_investor_if_missing, now carrying priority) -> if a stage was picked, link to the
pipeline at that stage (reusing applyStage's idempotent link-then-PATCH) -> if a reminder
title was given, POST /api/reminders keyed on the new row's source_row_id. The link and
reminder steps are non-fatal: a failure toasts but never loses the created investor, and a
create that returns no row id warns instead of a clean success.
Backend: handle_log_fundraising_communication honors an optional priority flag only on its
create-if-missing branch (an existing-row log never touches priority).
Guarded by test_grid_add_investor.py (priority-on-create, defaults-False, the create-branch-
only invariant, and the create->link / create->reminder handshakes on a freshly-synced row).
40/40 backend green; the create sheet was interaction-verified in a throwaway jsdom harness.