Capture city + LinkedIn on card intake; sharpen the transcription prompt

The card transcription prompt now reads emails/URLs/phones character-by-character,
explicitly forbids autocompleting toward a plausible domain (the mara.com ->
marac.com failure), and emits labeled lines (which also feeds the field extractor
cleaner input).

The extractor gains city + linkedin_url. city is a plain field (low-harm if wrong;
the human sees it on the card). linkedin_url follows the email-integrity rule: kept
only if it literally appears in the source / a revise instruction, never minted -- a
wrong profile URL points at the wrong person. Both flow to the contact via the
existing log-communication upsert (city also syncs to the grid contact pill).

Phone is intentionally NOT included yet: the bot's write path can't store it until a
small server-side change lands (next s9pk). See the matrix-intake guide.
This commit is contained in:
Keysat
2026-06-20 11:07:17 -05:00
parent 5e115a3409
commit 8b2eb01a65
8 changed files with 120 additions and 13 deletions
+12
View File
@@ -98,6 +98,18 @@ existing flow (parse → match → disambiguate → approve → `log-communicati
- **Provenance:** a card commit tags `source="matrix_card"` (vs `"matrix_intake"` for a typed note)
in the audit log, threaded via the proposal's `_source` control key (`handle_intake(…, source=…)`
`crm_client.build_commit_payload`, which defaults to `"matrix_intake"` when absent).
- **Fields captured** (`parse._FIELDS`): investor, contact, email, title, **city**, **linkedin_url**,
note. `city` is a plain extracted field (low-harm if wrong; the human sees it); `linkedin_url`
follows the **email-integrity rule** — kept only if it literally appears in the source/instruction,
never minted (a wrong profile URL points at the wrong person). Both ride to the contact via the
existing `log-communication` upsert (`_upsert_contact_from_fundraising` already honors them; `city`
also syncs to the grid contact pill, `linkedin_url` lands on the canonical contact record).
- **Phone is NOT captured yet (pending a server change).** The transcription reads the phone, but the
bot's write path (`_upsert_contact_from_fundraising`) doesn't accept a `contact.phone` today, so
phone is deliberately left off the card to avoid showing a field that won't persist. Enabling it is
a small additive server change (read `contact.phone` → write `contacts.phone`; contact-level, not a
grid pill field) that ships in an **s9pk** (version bump + install), paired with adding `phone` to
the bot's extractor/card/payload in the same deploy. Tracked agreed 2026-06-20.
- **UX:** the bot acks `📇 Reading the card…` before the (slower) vision call; an unreadable image
(model replies `NONE`, or transcription < 5 chars) gets a "try a clearer, well-lit photo" reply
instead of a garbage proposal.