Skip to content

feat: mobile polish, UI upgrades, contact cards, Iris improvements, contact verification#50

Merged
jp-bmn merged 38 commits into
mainfrom
feat/mobile-polish
Jun 22, 2026
Merged

feat: mobile polish, UI upgrades, contact cards, Iris improvements, contact verification#50
jp-bmn merged 38 commits into
mainfrom
feat/mobile-polish

Conversation

@m1lestones

Copy link
Copy Markdown
Collaborator

Summary

UI Improvements

  • Role color badges on contacts (CEO red, CFO blue, CRO green, VP purple)
  • Clickable signal cards — entire card opens source article
  • LinkedIn SVG logo replaces text link on contact cards
  • Envelope icon replaces raw email address on contact cards
  • Hover highlight on contact cards
  • Removed "Needs review" badge — hides cleanly when no LinkedIn
  • Visited link color fix on hospital list (stays teal after clicking)
  • Removed dev stats (last run / next run / stored) from signal feed footer

Iris Chat

  • Expand/collapse toggle on both mobile and desktop
  • Mobile panel is full-width, no overflow
  • Removed all emojis — fully professional
  • Combined no-emoji + anti-hallucination rules in system prompt

Dev Dashboard

  • Hidden behind double-click avatar easter egg
  • Contact verification panel — Approve / Find alternatives / Reject
  • Agent searches 5 lanes per role, Claude cross-references before confirming
  • High confidence (55%+) contacts auto-approved, lower saved as pending

Contact Restore

  • Multi-lane search script (LinkedIn, press releases, healthcare news, hospital website, general search)
  • Claude cross-references all lanes before inserting
  • Restored contacts across all 6 hospitals

Test plan

  • Visit any hospital profile — role badges, LinkedIn logo, envelope icon visible
  • Click a signal card — opens source article in new tab
  • Open Iris on mobile — panel full-width, expand icon in header works
  • Open Iris on desktop — expand icon opens 640px wide panel
  • Double-click avatar — navigates to /dev dashboard
  • /dev dashboard shows Contact Verification section with pending contacts
  • Signal feed footer shows only "View all hospitals" and pending review count

🤖 Generated with Claude Code

Built after a string of incidents this sprint that all had one thing in
common: nobody knew something was wrong until it was too late.

Real incidents that triggered this build:

API & Deployment
- Jun 16 10:02 PM — backend went down after PR #48 changed the Railway
  start command. Nobody knew for 4+ hours. Found at 2 AM by manually
  checking. This dashboard shows a pulsing red "API Down since 10:02 PM"
  pill the moment it happens.
- Joel's revert commit showed "Deployment successful" in Railway but the
  API was still returning 502. The agents service was running the cron
  worker (tsx src/index.ts) instead of the FastAPI server. Dashboard
  would have shown the API still down despite Railway saying online.

Signal Quality
- Test signal "Ignore Me 4" appeared in production for days undetected.
  Dashboard flags any signal matching /ignore|test signal/ instantly.
- Signal titles containing raw HTML tags slipped through. Dashboard
  scans all signals for /<[^>]+>/ and surfaces the count.
- Signals attributed to wrong hospitals — only caught by manually
  comparing signal hospital_id vs expected. Now visible per hospital.

Contact Health
- Jefferson Health showed 0 contacts — only found by hitting the API
  directly. Hospital Coverage panel flags any hospital with 0 contacts.
- LinkedIn post URLs (/posts/) were saved as profile URLs. Michael's
  pipeline was generating slugs by guessing from names. Contact Health
  panel counts broken LinkedIn URLs per hospital.
- AI error messages saved as contact names — scraper failed silently
  and stored the error text. Dashboard surfaces total broken contacts.
- Joseph Impicciche still showing as Ascension CEO months after retiring
  Dec 31 2025. Dashboard + LinkedIn verifier (PR #47) address this.
- Prior employer empty for all contacts — Danielle's original May 20
  request that went unnoticed for weeks.

Coordination
- "I fixed it locally" — Michael tested on his machine but not on prod.
  No way to verify without manually curling the API after every push.
  Dashboard auto-refreshes every 30s and shows live production state.
- Joel, Michael, and Juan were pinging each other constantly: "Did it
  merge?" "Is it live?" "Did the contacts update?" Dashboard answers
  all of these without anyone having to ask.

What the dashboard does:
- API Health — live ping every 30s, shows exact time it went down
- Total Signals + Pending Review — from /status
- Hospital Coverage — signal count per hospital, flags < 3 as warning, 0 as error
- Contact Health — contacts + broken LinkedIn URLs per hospital
- Signal Quality — HTML titles, test signals, null LinkedIn, post URLs
- Open GitHub Issues — live from GitHub API, grouped by assignee (Joel / Michael / Juan)
- Audit Log — timestamped session events (API up/down transitions)
- Dark/light mode toggle — persisted to localStorage

Admin only — shown in sidebar nav only when isAdmin = true.
Reads entirely from existing API endpoints, no new backend work needed.
@m1lestones m1lestones requested a review from jp-bmn as a code owner June 21, 2026 00:27
This directly answers the 15-tabs problem from the pitch.

Before: Danielle hit the review queue and saw a title + AI summary
with no way to verify — one source, no context, blind approval.

After: every signal card now shows the original article link so she
can read the actual story. And with one click on "More coverage" she
gets 3–5 articles on the same story pulled live from Becker's, Modern
Healthcare, Fierce Healthcare, Healthcare Finance News, and the broader
web — all in the same card, no tab-hopping.

The whole pitch is built around replacing 15 tabs with one place.
This is that promise made real inside the review queue. She doesn't
just approve a summary — she reads the story, cross-references coverage
from multiple publishers, and makes a real call. Human judgment with
AI doing the legwork.

New API route: /api/signals/coverage
- POST { title, hospital_name }
- 3 parallel Serper searches across healthcare publications + general web
- Deduplicates by URL, returns up to 5 results with source domain + snippet

Joel — this is on feat/mobile-polish. Merge PR #50 when ready.
Danielle's review queue now visually separates real signals from noise:

- Real signals (green border) appear at the top with Approve, More coverage, Dismiss
- Noise signals (grayed out) are pushed to the bottom with an "Irrelevant — dismiss all" separator
- "Dismiss all" button wipes all noise in one click
- Each noise card has a "Move to relevant" button in case the AI mislabeled a good article

---

Joel — backend bug to fix before demo:

Some signals are showing up with a 75% confidence score AND tagged as
filtered_out at the same time. These contradict each other. A 75% score
means the AI thinks the article is relevant — filtered_out means it flagged
it as noise. The pipeline is sending both on the same article.

Example: https://www.fiercehealthcare.com/providers/ftc-clears-ascensions-39b-amsurg-purchase-requires-some-asc-divestitures
This is a real Ascension M&A story but lands in noise because filtered_out
overrides the score on our end.

Fix: if signal_type or tier is filtered_out, confidence score should be
below 30% so it gets skipped at ingest and never reaches the review queue.
Danielle has "Move to relevant" to rescue mislabeled articles for now but
the root cause needs to be fixed in the pipeline.
Signals tagged filtered_out (tier or signal_type) now excluded before
display and category filtering — garbage never reaches the feed.
When the AI search doesn't find a LinkedIn URL, show a paste field so
the reviewer can add it before approving. Approve sends it as an override
to the PATCH endpoint so it's saved with linkedin_verified: true.
DigestTracker now captures ae_id from URL params (?utm_source=digest&ae_id=xxx)
instead of using a hardcoded ID. Views stored in Supabase digest_views table
and forwarded to backend endpoint. DigestRoster shows Danielle each AE's
last digest open time on the home page admin view.

Requires Joel to:
1. CREATE TABLE digest_views (ae_id uuid PRIMARY KEY, digest_id text, viewed_at timestamptz DEFAULT now());
2. Append ?utm_source=digest&digest_id={id}&ae_id={ae_id} to all digest links
@jp-bmn

jp-bmn commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Updates approved

@jp-bmn jp-bmn merged commit 698ca91 into main Jun 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants