kelly-lead-funnel · git:20260806.bed1b85 · 2026-08-06 · sha256 2d526b63b73a7194

kelly-lead-funnel git:20260806.bed1b85A

Immutable. This exact content is served forever at /api/v1/blob/2d526b63b73a7194.

---
name: kelly-lead-funnel
description: Busabase-backed App-in-Skill control panel / kanban board for a BD or sourcing team triaging merchant and business financing leads for a lender or investment fund. Use when the user invokes $kelly-lead-funnel or /kelly-lead-funnel, wants to review the deal sourcing pipeline, funnel, lead board, or asks to move a lead's stage, reject a lead, add a note, score a lead, or see funnel conversion rates. Deterministic rule-based lead scoring only — never an LLM call — and never sends outreach, signs term sheets, or moves money.
---

# Deal Sourcing Funnel

## Overview

Kelly Lead Funnel is a Busabase Cloud App-in-Skill. Its canonical product
surface is the AirApp in Busabase, not a separate local-data product. The
same Hono source supports an explicitly requested local preview with OAuth
connection bootstrap. It gives a BD/sourcing team a kanban board plus a
per-lead detail panel to triage merchant/business financing leads (merchant
cash advance, revenue-based financing, or similar) for any lender or
investment fund: move a lead's stage, reject with a reason, and leave notes —
all direct, immediate writes to Busabase.

This is a control-panel/kanban App-in-Skill, not a review-then-approve queue:
there is no AI-authored draft to approve and no separate execute/decisions
step. The score and suggested next action are computed by a documented
rule-based function (`app/app/js/lead-funnel-model.js`, ported from the
retired `lib/scoring.ts`); the human sourcing-team operator makes every
stage/reject/note decision directly in the kanban UI, the same way
`kelly-crm`'s kanban stage moves work.

Default behavior is AirApp-first. Unless the user explicitly asks only for
explanation, give the user the clickable AirApp URL. Start localhost only
when local preview/debugging is explicitly requested; it uses the same
Busabase resources and never offers another data provider. Use chat-only mode
only when the user says "纯聊天", "chat only", "不要打开 UI", or similar.

## Mandatory Dependencies

1. Read and follow `$kelly-app-skill-creator` for product behavior, visual
   quality, responsive layout, and the complete canonical `app/` artifact.
2. Read and follow `$busabase` for connection, target Space, node discovery,
   ChangeRequests, review, and merge behavior.
3. Read and follow `$busabase-app-creator` for resource modeling, AirApp
   runtime limits, security, validation, and deployment.

If a dependency is unavailable, preserve this skill's artifact and product
contracts, stop before the unavailable Busabase operation, and report the
exact missing dependency. Do not invent a second data backend.

## App UI Screenshots

<table>
  <tr>
    <td width="50%"><img src="assets/screenshots/overview.webp" alt="Deal Sourcing Funnel overview"></td>
    <td width="50%"><img src="assets/screenshots/kanban.webp" alt="Deal Sourcing Funnel kanban board"></td>
  </tr>
  <tr>
    <td><strong>Overview</strong><br>Funnel summary header with per-stage counts, conversion rates, and rejection rate.</td>
    <td><strong>Kanban board</strong><br>Leads across New → Data-Verified → Scored → Term-Sheet-Ready → Rejected, with a score chip per card.</td>
  </tr>
  <tr>
    <td colspan="2"><img src="assets/screenshots/lead-detail.webp" alt="Deal Sourcing Funnel lead detail"></td>
  </tr>
  <tr>
    <td colspan="2"><strong>Lead detail</strong><br>Score breakdown by factor, suggested next action, notes, stage-move actions, and reject-with-reason.</td>
  </tr>
</table>

## Boundary

- Deterministic, rule-based lead-quality score only
  (`app/app/js/lead-funnel-model.js`). NEVER call an LLM to score, rank, or
  auto-reject a lead.
- The AirApp reads and writes its own Busabase Bases only; it never sends
  outreach, emails, signs term sheets, disburses funds, or touches any
  external system. There is no trusted execute/handoff script — the direct
  kanban writes (stage move, reject, note) are the full extent of what
  happens.
- Generic, brand-neutral tool: never hardcode or reference a specific real
  company, lender, or fund name in code, templates, or docs.
- Treat lead financial data as sensitive. Never commit real lead data, a
  local credential file, or Busabase secrets.

## Busabase Resources

Two Bases under one application Folder (`kelly-lead-funnel`), declared in
`app/app/js/config.js` and `app/resource-map.json`:

- `leads`: one row per merchant/business lead — brand name, category, city,
  store count, est. monthly revenue, lead source, data verifiability, funnel
  `stage`, `rejection-reason`, `notes` (JSON array), and `stage-history`
  (JSON array). `score`/`score_breakdown`/`suggested_action` are never
  stored — they are pure/derived from these fields plus the fund's
  `scoring_criteria` and recomputed on every read, so a criteria change is
  reflected immediately with no staleness to manage. Stage moves,
  rejections, and notes are written directly onto the lead's own record by
  the human sourcing-team operator in the kanban board.
- `settings`: sanitized fund profile and scoring-criteria config (no
  secrets), one row keyed by `kind`.

Resources provision lazily through an idempotent Busabase ChangeRequest the
first time the app runs in a Space; see `references/lead-schema.md` for exact
field shapes.

## Scoring

`app/app/js/lead-funnel-model.js` (`scoreLead`) computes a deterministic
0-100 score from four weighted, explainable factors — chain-size fit (30),
revenue-scale fit (30), category risk (25), and data verifiability (15) —
against the fund's `scoring_criteria` (ideal store-count band, ideal
monthly-revenue band, category risk tiers). `suggestNextAction` maps the
score and stage to a deterministic next step. Every point on the 0-100 scale
is explained by a `score_breakdown` row (factor, weight, contribution,
rationale) so the UI can show a transparent breakdown, never a bare number.

## Direct Kanban Actions

All human actions write straight onto the lead's own Busabase record through
`busabase-sdk`, exactly like `kelly-crm`'s kanban stage moves — there is no
approval queue and no separate decisions bucket:

- **Move stage**: advance a lead through New → Data-Verified → Scored →
  Term-Sheet-Ready. Appends a `stage-history` entry.
- **Reject**: move a lead to `rejected`. Always requires a `reason`, which is
  stored on the lead and also recorded in `stage-history`.
- **Add note**: append a timestamped note to the lead.

From a standalone local preview the write merges immediately (trusted
operator); from the deployed AirApp it creates a pending ChangeRequest for
the trusted process to merge, per the AirApp boundary in
`$busabase-app-creator`.

## Demo Mode

- `?demo=1` or `?demo=board` opens a deterministic, fully offline mock
  pipeline (21 leads across all 5 stages, ported verbatim from the retired
  `lib/mock-leads.ts`) for documentation and screenshots.
- `?demo=lead` opens the first lead's detail pane; `?demo=settings` opens
  Help & Settings.
- `lang=en` or `lang=zh` forces UI chrome language for screenshots.
- Demo mode never reads or writes Busabase and never claims a real
  connection; demo actions (move/reject/note) are read-only and raise an
  error if attempted.

UI language: English and Chinese chrome with `Auto` default.

## Local App

Default behavior is AirApp-first — give the user the clickable AirApp URL.
Start `pnpm --dir app dev` only when local preview/debugging is explicitly
requested.

## Views

- `#/board`: funnel summary header + kanban across all 5 stages.
- `#/board/<stage>`: single-stage filtered list (used by the human-attention
  shortcuts for New / Scored / Rejected).
- `#/leads/<id>`: score breakdown, suggested next action, notes, stage-move
  buttons, reject-with-reason.
- `#/settings`: sanitized fund profile and scoring-criteria summary.

## Completion Criteria

Finish only when:

- the skill contains the complete canonical `app/` project and
  `pnpm --dir app dev` remains supported;
- all persistent config, state, and domain data use `busabase-sdk` and the
  declared resource map — no local JSON, browser storage, or provider
  choice;
- Vault values and API credentials never reach browser-visible surfaces;
- local setup offers Cloud/custom URL OAuth plus the explicit Demo path,
  while a deployed AirApp uses its ambient session;
- Board, lead detail, and Help & Settings render on desktop and phone widths;
- `pnpm --dir app run check` and `node --test` pass.

## Stop Conditions

Stop before consequential Busabase mutation when the target Space is
ambiguous, the current user lacks permission, or a same-slug resource is not
application-owned. Never send outreach, sign a term sheet, or move money from
the AirApp.