The day my portfolio started answering back. A chatbot in my voice, a scorer and a digest, shipped solo.
Role
Solo: product, design, prompts, code
Years
2025 → 2026
Stack
Next.js 16 · Claude · Upstash · Resend · Vercel
Status
In production on albruv.com
Published · Updated
2025 → 2026
layers: chat, scorer and digest
3
One system
axes scored on every conversation
5
Fit · legitimacy · value · timing · ask
from the end of a chat to a verdict
3 min
Then to my inbox
pages of system prompt to make it sound like me
~38
The real work
In short
Álvaro Velasco shipped the chatbot, scorer and digest behind albruv.com solo, end to end, across 2025 and 2026 using Claude Code: a conversational front end (Albot) in his voice, a second-pass scorer that reads each conversation, and a digest that emails him only the leads worth his time.
01 · Assumption
A portfolio is something people read.
Recruiters skim. Founders want to know one specific thing. Most of what a portfolio says, nobody asked.
02 · Decision
Let people ask instead.
Albot answers questions about my work in my voice. A second pass reads each conversation about three minutes after the visitor stops typing and scores it on five axes: fit, legitimacy, value, timing and whether they asked explicitly.
Only the conversations worth my time reach my inbox, as a five-second triage card. Drops are filed silently.
I owned the product strategy, design, prompt and system logic, lead scoring, frontend, backend and deployment. The team was me and Claude Code.
The first version put personality, facts, rules and stories in one long document. I separated the voice and policy from the facts. The redesign uses a curated knowledge pack validated at build time.
Chat, scorer, digest.
01
Chat
Visitor talks to Albot. Albot replies in my voice.
Visitor
in browser
Albot UI
Next.js
Claude Sonnet
system prompt = me
↓transcript
02
Score
A second pass reads the conversation after the idle delay, or immediately on close or booking. It returns structured fields.
Transcript
from Layer 01
Claude Haiku
scorer prompt
Redis
scored fields
↓on close / booking / idle delay
03
Digest
When a conversation matters, I get an email I'll actually open.
Composer
score + transcript
Resend
email API
My inbox
iCloud
The three layers, end to end. Each one runs on its own trigger.
Redacted example
From:
Albruv Chatbot <onboarding@resend.dev>
To:
alvaro@albruv.com
Subject:
Very High · Sarah at [Series B AI startup], needs senior IC for AI prod…
Priority: 🟢 Very High
Fit: yes
Company: [REDACTED], verified: yes
Contact: Sarah, Head of Product
Seat: full-time, senior IC
Why now: shipping 2 AI features in next quarter,
design lead just left
Outcome: get the AI product surface to feel like
one product, not three
Value: Series B, $24M raised
Timing: ASAP, interviewing this month
Asked: yes (explicit intent)
Flags: none
Visitor email: sarah@[REDACTED].com
Company context: verified via web search
Conversation snippet:
> "We're shipping two AI features this quarter and
> our design lead just left. We need someone senior
> who can both ship and lead, and who actually gets
> AI product UX. Are you taking calls?"
Full transcript:
https://www.albruv.com/api/chat/debug/transcript/[ID]
A redacted Very High lead, in the format the digest layer actually sends. five-second triage header, conversation snippet, transcript link. Drops never reach the inbox.
ALBOT
// answers for Álvaro, in his own wordsonline
albot>
I'm Albot. Álvaro built me so you don't have to read his whole portfolio. Ask me anything about his work.
you>
What was the real problem at SellerCrowd?
albot>
The brief was information architecture, and the redesign did fix it. But contributions, the metric that mattered, stayed flat. The real lever was incentives: gems, VIP access, one-click contribution. Monthly contributions went from 360 to 23,770. read the case
[ show me vecinOS ][ is he available? ][ book a call ]
>ask about his work⏎
albot can be wrong. Álvaro reads the conversations that matter.
Albot types, then answers. It can be wrong, and it says so.
03 · What broke
Every fix is in production.
iCloud bounced my test emails until I learned what SPF and DKIM were and declared them properly.
The first scorer was too eager and emailed me in the middle of conversations. Now it waits three minutes after the visitor stops typing.
The scheduler refused my server seven times for different reasons: addresses, identifiers, regions, encoding and a network hiccup. Each fix taught me something a tutorial would not.
The scorer correctly dropped my own fake test leads without emailing me. The filter was doing exactly what I had written into the specification.
Historical excerpt
src/prompts/system.md
v1.9.2
## Before every reply: the decision loop
Run this list top-down. Stop at the first
match and act on it.
1. Explicit contact intent? (Asked how to reach
me, for my email, for a meeting.)
→ Offer handoff. Stop.
2. Disengagement signals? (Terse, distracted,
"just browsing," clipped one-word replies,
"this is irrelevant," repeated pushback.)
→ Short verdict + soft handoff. Stop.
3. Unsafe, private, unverified, or explicitly
deflected topic? (Comp, references, family,
unverified facts.)
→ Reduce detail or deflect. Don't invent.
Stop.
4. Is a discovery beat due?
→ Ask exactly that beat. Stop.
5. Is the soft email ask eligible and not
yet placed? (Real signal landed, ≥ 2
discovery turns deep, no beat is due.)
→ Ride a statement-form email ask.
Stop.
6. Otherwise → Answer naturally.
Hard constraints on every reply:
≤ 200 words, at most one question mark,
first person as Álvaro, no em-dashes,
no preamble.
The decision loop Albot ran before every reply. The full original prompt ran to about 38 pages.
The lesson
The bottleneck was never the code.
It was writing the system prompt that tells the bot how to be me: about 38 pages of voice, judgment and boundaries.