Capture phone (office) + mobile (cell) on card intake; ship v0.1.0:98

Completes business-card contact capture. The transcription prompt now labels
Phone/Mobile/Fax on separate lines, and the extractor maps an office/main number ->
phone and a cell -> mobile, never a fax. Both carry the same digit-in-source
integrity rule as email/LinkedIn: a number is kept only if its digits literally
appear in the source (or, on revise, the instruction) -- never minted. The proposal
card shows Phone + Mobile and they're editable (aliases phone/tel/office, mobile/cell).

Server: _upsert_contact_from_fundraising now accepts contact.phone + contact.mobile
and writes them to the canonical contact record (contact-level, not grid pills),
shipped in s9pk v0.1.0:98. No schema change -- the contacts columns already exist.

41/41 backend suite green + the matrix_intake units; card flow end-to-end is live-smoke.
This commit is contained in:
Keysat
2026-06-20 11:26:39 -05:00
parent 92ab59de4e
commit e824ff2206
12 changed files with 139 additions and 29 deletions
+11 -10
View File
@@ -165,22 +165,23 @@ def main():
# The Matrix card flow sends these on the contact dict; the upsert must persist them.
print("\n[contact fields: phone + city + linkedin persist on the contact]")
st, d = _req(port, "POST", "/api/fundraising/log-communication", token, {
"investor_name": "MARA Holdings", "create_investor_if_missing": True,
"contact": {"name": "Doug Mellinger", "email": "doug@mara.example",
"phone": "1.914.456.2146", "city": "New York",
"linkedin_url": "linkedin.com/in/dougmellinger"},
"investor_name": "Fortitude Investment Group", "create_investor_if_missing": True,
"contact": {"name": "Daniel Raupp", "email": "draupp@fortitude.example",
"phone": "631-474-5610", "mobile": "631-922-1195", "city": "Setauket, NY",
"linkedin_url": "linkedin.com/in/danielraupp"},
"type": "note", "body": "from a business card", "append_note": True,
})
check(st == 201, f"create with contact fields -> 201 (got {st})")
c = _db()
crow = c.execute("SELECT phone, city, linkedin_url FROM contacts WHERE lower(email) = ?",
("doug@mara.example",)).fetchone()
crow = c.execute("SELECT phone, mobile, city, linkedin_url FROM contacts WHERE lower(email) = ?",
("draupp@fortitude.example",)).fetchone()
c.close()
check(crow is not None, "contact row exists")
check(bool(crow) and crow[0] == "1.914.456.2146", f"phone persisted (got {crow[0] if crow else None!r})")
check(bool(crow) and crow[1] == "New York", f"city persisted (got {crow[1] if crow else None!r})")
check(bool(crow) and crow[2] == "linkedin.com/in/dougmellinger",
f"linkedin persisted (got {crow[2] if crow else None!r})")
check(bool(crow) and crow[0] == "631-474-5610", f"phone (office) persisted (got {crow[0] if crow else None!r})")
check(bool(crow) and crow[1] == "631-922-1195", f"mobile (cell) persisted (got {crow[1] if crow else None!r})")
check(bool(crow) and crow[2] == "Setauket, NY", f"city persisted (got {crow[2] if crow else None!r})")
check(bool(crow) and crow[3] == "linkedin.com/in/danielraupp",
f"linkedin persisted (got {crow[3] if crow else None!r})")
# ── unknown source_row_id is refused (guard) ──
print("\n[guard: reminder on an unknown source_row_id -> 404]")