~/caro/portfolio/2026

Rethinking time-to-value
in B2B SaaS onboarding

brand

role

Product
Design system
Data model
AI orchestration
Build & deploy

tools

Cursor, Claude Code
Next.js 16
React 19, Tailwind v4
Prisma & Supabase
Claude API
Resend
Playwright
Vercel

summary

Vector helps vendors cut time-to-value and stop first-90-day churn. It's a shared vendor/customer workspace with an AI layer that does the tedious work: drafting follow-ups, turning meeting transcripts into tasks, and surfacing risk before an onboarding goes sideways.

setting the stage

I noticed a problem: procured tools promise value, then lose momentum at onboarding. Vendor-customer context gets lost in a sea of meetings and time-to-value slips. While playing with agentic engineering tools and wanting to get better at building, I jumped into Claude Code and built a tool to tackle the problem I saw.

Vector's AI Insights view: a streamed summary of one onboarding with risks, wins, focus for today and the week.
All data is fictional; company names and logos are used for illustrative purposes only and do not represent real customer endorsements

My role

Hairline lightbulb icon with mint ray dots on a faint outlined card

product >

I conducted market research, set the ICP, scoped the product and made the feature bets.

Hairline paint palette icon with coloured paint dots on a faint outlined card

design system >

I created Vector's visual identity and a single source of truth for the design system.

Hairline plug connector icon with peach spark dots on a faint outlined card

architecture & build >

Agents wrote the syntax. I researched and approved every architecture and build call.

Hairline node diagram icon, lilac squares with orange connectors, on a dark card

AI orchestration >

I designed the AI layer, from grounded prompts to streaming tool use and observability.

Problem space

Follow-up falls
through the cracks

Onboarding a new B2B tool is follow-through. Most of it is agreed on calls and action points often get muddled and lost. The teams who feel this most are start-ups and scale ups that are stuck between two bad options.

too heavy

Enterprise tools

Rocketlane and GuideCX are built for large implementation teams, they are expensive, and take months to roll out.

too manual

Hacked PM tool

Teams hack onboarding into Notion, Linear or Asana. Nothing tracks health, the customer has no real view, and the follow-up work is lost.

Most onboarding tools optimise for the vendor's internal project management. I optimised for the shared experience, and let AI handle the follow-up that usually falls through the cracks.

/shared board

The product

Shared view and AI
that keeps work on track

Vendor board

Every onboarding runs as a Kanban board. I made the columns phases instead of statuses, so the board reads as the journey from kickoff to go-live, and progress lives on the task as a tag.

Vendor and customer work from the same board. Both see the full plan, and the customer's portal focuses on their own tasks.

Vector's vendor board: an onboarding as a Kanban board, phases as columns, tasks carrying owner, due date and status.

The customer clicks a magic link and lands straight on their tasks. No account, no password, no training. Every visit is tracked, so the vendor knows the moment they go quiet.

Customer portal

The customer sees their progress and what needs their attention this week. The portal highlights their own tasks, so their work cuts through the noise.

Vector's customer portal: the customer's own tasks, days to go-live, and a plain-language summary of where things stand.

Notifications centre

Everything the customer does flows back. A completed task, a new comment and a first portal visit each land in the vendor's notification centre, and one person's changes collapse into a single entry to keep the noise down.

Email is saved for what needs the most visibility. Portal invites, task assignments and comment alerts go out through Resend, so they reach the customer where they already work.

Vector's notification centre: customer activity grouped by actor, showing completions, comments and first portal visits.

notification routing / lib/db.js

task completednew commentportal visitportal invitetask assignedtask commentedemitActivity()notification centreemail / resend

/predictive health

Predictive health

Every onboarding is scored On track, At risk or Blocked. The triggers are all real task data, blocked or overdue work, a pace that overruns the go-live date, a third of the tasks stuck. I kept AI out of the scoring on purpose. It's deterministic JavaScript, so the same input always gives the same answer.

Vector's health table: each company scored On track, At risk or Blocked, with task counts and how many are blocked.

lib/health.js

// no model in the loop, only task data
const BLOCKED_THRESHOLD = 0.3;

if (blockedPct >= BLOCKED_THRESHOLD) {
  status = "Blocked";
  reasons.push(
    `${blockedCount} of ${total} tasks blocked`);
}
// every flag carries its evidence
return { status, reasons };

Every flag arrives with its evidence. 3 of 9 tasks blocked, 8 tasks overdue, customer dark for 64 days. When you can see why the flag was raised, you can act on it.

/ai admin

AI overview

Two views, because different roles need different depth and nobody needs everything at once.

The portfolio high level view shows which onboardings need attention. Inside an onboarding it gets granular, with focus for today, this week, risks and wins. Every claim is anchored to a real task id.

Vector's AI insights for one onboarding: summary, risks, wins, and a prioritised focus list, each item citing a task id.

Turning meetings into tasks

What was agreed on a call is the easiest thing to lose in an onboarding, so Vector writes it up instead.

I integrated an AI notetaker, Miniti. When a call ends it fires a webhook with the transcript, and a tool-use orchestrator reads it and drafts task creations, status changes and reassignments. It reads the board first, so work you already track becomes an update to the existing task instead of a duplicate.

Vector's Actions queue: a task drafted from a meeting transcript, marked 'From miniti', with create, edit and dismiss.

miniti → vector

  1. call ends

  2. pass 1 / extraction

  3. pass 2 / orchestrator

  4. review queue

Automated follow-ups

A weekly scanner on Vercel Cron walks every active onboarding and flags any task blocked or more than five days overdue. Each flag becomes a drafted email that only the task's owner sees, one approval away from sending.

A task with no owner gets no follow-up. I chose clear responsibility over copying everyone in, and kept one plain tone for now. Adapting the writing to each user's voice is on the roadmap.

Vector's draft follow-up: an AI-written email grounded in a blocked task, with dismiss, open in mail and comment.

lib/ai/scan-stale.js

// vercel cron: Mondays 08:00, "0 8 * * 1"
const stale = tasks
  .map((t) => withStaleness(t, today))
  .filter((t) => t.staleness !== null)
  // no owner, no follow-up
  .filter((t) => t.ownerId != null)

I gave the AI a review queue instead of write access to the board. It drafts, and the user decides whether to approve, edit or reject any task or follow-up.

The matching

Whose meeting was this?

signal #01

Attendee domains

When a meeting has an invite list, a matching customer email domain is the strongest signal.

signal #02

Contact emails

Failing that, each attendee's email is looked up against every onboarding's contacts.

signal #03

The title

The title is scanned for the significant words of a company name e.g. “Raycast weekly sync”.

signal #04

The transcript

The last resort: summary, topics, notes and transcript are searched for a customer mention.

no signal

Needs your input

Zero or more than one candidate and the meeting lands in “to assign the inbox”.

The AI layer

Grounded, efficient
and observable

I designed the AI overview to keep the model away from hallucinated facts. Plain JavaScript computes the hard signals (overdue counts, velocity, customer engagement) from a small snapshot of the board. The model owns the narrative, and the guardrails stop it from inventing.

lib/ai/context.js
// Layer 1: deterministic code builds a snapshot.
// Claude reads it. It never does the arithmetic.
{
  "onboarding": { "company": "Initech",
                  "daysToTargetGoLive": -121 },
  "facts": {
    "totalTasks": 9,
    "tasksDone": 1,
    "tasksOverdue": [
      { "taskId": "IN-4", "daysOverdue": 12 },
      …1 more
    ],
    "health": "Blocked",
    "healthReasons": [
      "3 of 9 tasks blocked",
      "2 tasks overdue",
      "Past go-live date with open tasks"
    ]
  }
}
lib/ai/insights.js
// Layer 3: verbatim, rules 2-3 and 6-10 elided.
const ONBOARDING_RULES = `
RULES — non-negotiable:
1. NEVER invent facts. Every claim must reference
   a specific taskId or named field from the
   snapshot.
4. \`risks[]\` (max 3): each item is concrete
   evidence something will go wrong, with severity.
   "Could become a problem later" without evidence
   is NOT a risk.
5. \`wins[]\` (max 2): each item is a real event
   from the snapshot's \`recentWins[]\` field
   (last 7 days). Do NOT invent.
`;

Every call is prompt-cached, pinned to a JSON schema and logged with its cost, and an unchanged board never pays twice.

Observability

Every call logged,
every failure visible

I shipped backend dashboards to track Vector's AI features. It monitors latency and cost and helps me pinpoint and troubleshoot errors. The traces let me see where retrieval breaks down and which step to optimise.

usage by feature / admin · last 30 days

kindcallserrorstotal costp95cache hit
insight_onboarding212$1.843.1s78%

one call, kept in full

tokens · 4 812 in · 3 921 cache read · 391 out

cost / duration · $0.0041 · 2.9s

request id · req_011CSHn3xAzKq…

insight_portfolio64$0.714.2s81%
miniti_extraction581$0.928.4s46%
miniti_orchestrator58$0.606.1s52%
scan_stale_followup33$0.192.3s88%

the pipeline view / admin

All 24Processed 19Ambiguous 2Stuck 1Errored 1Test 1
  • Raycast weekly sync

    processed08/07/2026

  • Untitled meeting

    processed06/07/2026

    matched · Function Health, via the transcript (title gave nothing)

    pass 1 / extraction · 5 claims, each with a verbatim source quote

    raw extraction JSON

    pass 2 / tool calls · create_task ×2 · match_existing ×2 · update_status ×1

    raw tool calls JSON

    drafts · 5 in the review queue · $0.036

    full transcript · 20 utterances

  • Modal kickoff call

    ambiguous07/07/2026

  • beehiiv dashboards review

    errored04/07/2026

    Pass 2 timed out after 60s

Under the hood

Putting together Saas
product from scratch

I built Vector to grow my range as a designer who can also ship. Every architectural decision was a conscious choice and a learning experience.

next.js 16

app router, plain JavaScript

react 19

server components first

tailwind v4

tokens compiled from one documented file

prisma 7

16 models, cascade deletes, one data path

supabase

postgres + vendor auth (customers get magic links)

claude api

sonnet 4.6, prompt-cached, schema-pinned, streamed

resend

customer email, dark-theme templates

playwright

e2e on the flows I cannot afford to break

vitest

unit tests on the pure logic, green in CI

vercel

deploys, plus the weekly cron

Working with AI

Designing how my team works

the pair >

builderevaluatorbuild →← critique

Most features ran as a loop. One agent built, a second reviewed the work for issues and against the plan.

the team >

designerdeveloperceothe decision

For the calls that shaped the product I set up a teams e.g. a designer, a developer and a CEO each judging from different angles.

the fleet >

wt/1wt/2wt/3main

Workstreams ran in parallel git worktrees, each with its own group of agents, so the build never waited on a single conversation.

the plan >

goaloptionsmy callbuild↺ ask me follow-up questions

I never asked the AI to just build it. I set the goal, asked for options with tradeoffs, and made every call myself.

the net >

task idsdraft editsportal authplaywrighte2e

The flows I cannot afford to break run through Playwright end-to-end tests. Vitest unit tests cover the logic underneath.

the memory >

a decisiona wallskill.mdthe next session loads it

Design rules and conventions became skills the AI loads every session. The walls we hit got transformed into skills too.

What's next

Measured accuracy,
connected tools

AI accuracy >

The pipelines, the observability and a 30-case golden dataset are already built. The next step is measuring how accurate the drafts really are and iterating on the prompts.

sync with linear >

Engineering teams already run on Linear. A two-way sync would keep both sides current. Issues raised in Linear appear in Vector, and tasks created in Vector land back in Linear.

context from attio >

By the time onboarding kicks off, the customer's story already lives in the CRM. Pulling that context in from Attio means every onboarding starts informed.

View next project

Designing an AI brain
for a support call centre

CHECK IT OUT