RBF Portfolio Health Dashboard
Overview
Kelly Portfolio Health 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 fund/credit-desk operator a
read-mostly dashboard over a revenue-share / private-credit book: many small
SME (small/medium enterprise) contracts, each an advance repaid as a share
of the SME's future revenue up to a cap. The app aggregates the book into a
top-line health summary, a repayment-progress-vs-time-elapsed view, an
industry/city concentration breakdown, and a watchlist of contracts with a
recent revenue decline. The only human action is lightweight: flag a
contract for review, clear a flag, or leave a note — everything else is a
read view.
This is deliberately generic and brand-free: no real company, fund, or
SME name appears anywhere in the code, config, or seed data.
This is a direct-manipulation dashboard, not a review-then-approve queue:
there is no AI-authored draft to approve and no separate execute/decisions
step. Totals, repayment lag, concentration, and the watchlist are computed
by a documented, deterministic function
(content/kelly-portfolio-health-app/app/js/portfolio-model.js, ported from the retired
content/kelly-portfolio-health-app/server/insights.ts); the human flags/clears/annotates a contract
directly in the UI, writing straight onto the contract's own Busabase record
— the same way kelly-llm-gateway's rollout promote/rollback/hold and
kelly-lead-funnel'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
- Read and follow
$kelly-app-skill-creator for product behavior, visual
quality, responsive layout, and the complete canonical content/kelly-portfolio-health-app/ artifact.
- Read and follow
$busabase for connection, target Space, node discovery,
ChangeRequests, review, and merge behavior.
- 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
Boundary
- Read-mostly aggregation and human review flags only. NEVER contact any
external system, brokerage, payment processor, or SME directly. NEVER move
money, disburse funds, or change contract terms. There is no transaction
path in this skill by design.
- The AirApp reads and writes its own Busabase Bases only; it never calls a
live portfolio-servicing API. There is no execution/merge step beyond the
direct write itself — a human still applies the decision in the real
system of record.
- Contracts are never created by the AirApp; they enter Busabase through an
external portfolio-sync process, the same way
kelly-llm-gateway's routes
and kelly-lead-funnel's leads enter through an upstream process the app
doesn't control. The AirApp only ever updates an existing contract's
flagged/note/decision-updated-at fields.
- Generic, brand-neutral tool: never hardcode or reference a specific real
company, fund, or SME name in code, templates, or docs.
- Treat contract-level revenue and repayment data as sensitive. Never commit
a local credential file or Busabase secrets.
Busabase Resources
Two Bases under one application Folder (kelly-portfolio-health), declared
in content/kelly-portfolio-health-app/app/js/config.js and the generated template sidecars under content/:
contracts: one row per RBF/private-credit contract — funding terms
(funding-amount, cap-multiple, cap-amount), cumulative-repayment, a
6-month monthly-revenue series (JSON array), status, and the human
review flagged/note/decision-updated-at fields written directly onto
the same row. Totals, repayment lag, concentration, and the
revenue-decline watchlist are never stored — they are pure/derived from
these rows and recomputed on every read.
settings: sanitized config (fund name, base currency, risk-policy
thresholds), one row keyed by kind.
Resources provision lazily through an idempotent Busabase ChangeRequest the
first time the app runs in a Space; see references/portfolio-schema.md for
exact field shapes.
Portfolio Health Model
content/kelly-portfolio-health-app/app/js/portfolio-model.js (computeInsights) is ported verbatim from
the retired content/kelly-portfolio-health-app/server/insights.ts:
- Repayment lag — per contract,
expected_pct (months elapsed / term)
vs. actual_pct (collected / cap); lag_pp = expected_pct - actual_pct,
with a severity of ok | watch | high driven by
risk_policy.lag_watch_pp / lag_high_pp (defaults 15 / 25 percentage
points).
- Concentration — funding-amount concentration by category and by city,
as a percentage of active-contract AUM.
- Watchlist — a contract qualifies when its most recent month's revenue
is at least
risk_policy.revenue_decline_pct (default 10%) below the
average of its prior months (minimum 4 months of history).
- At-risk count — any contract with a non-
ok lag severity, or
status: delinquent.
No randomness, no ML — the same snapshot always produces the same insights.
Direct Contract Decision
The human action writes straight onto the contract's own Busabase record
through busabase-sdk, exactly like kelly-llm-gateway's rollout writes —
there is no approval queue and no separate decisions bucket:
- Flag for review / Clear flag: toggles
flagged ("true"/"false").
- Save note: sets
note (free text).
Both stamp decision-updated-at with the current time. 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 opens a deterministic, fully offline mock portfolio (~52
contracts across 8 categories and 10 cities) with computed insights, for
documentation and screenshots. It never reads or writes Busabase and never
claims a real connection; demo flag/note actions only update the
in-memory snapshot already rendered in the browser tab.
?demo=overview, ?demo=concentration, ?demo=watchlist select named
demo scenes/routes.
lang=en or lang=zh forces UI chrome language for screenshots.
UI language: support English and Chinese chrome with Auto default.
Local App
Default behavior is AirApp-first — give the user the clickable AirApp URL.
Start pnpm --dir content/kelly-portfolio-health-app dev only when local preview/debugging is explicitly
requested.
Views
#/overview: total AUM, total collected, weighted-average repayment
progress, at-risk count, category allocation donut, and the contracts most
lagging their expected repayment pace.
#/contracts: sortable table (business, category, city, funding amount,
actual progress, lag, status).
#/contracts/<id>: per-contract detail — funding/cap/collected, expected
vs. actual progress, a revenue sparkline, and the flag/note action.
#/concentration: funding-amount concentration by category and city.
#/watchlist: contracts with a recent revenue decline, each with a
sparkline and a flag-for-review / clear-flag action.
#/settings: sanitized configuration summary — data provider, fund name,
base currency, and risk-policy thresholds. Never expose secret values.
Completion Criteria
Finish only when:
- the skill contains the complete canonical
content/kelly-portfolio-health-app/ project and
pnpm --dir content/kelly-portfolio-health-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;
- Overview, Contracts, Concentration, Watchlist, and Help & Settings render
on desktop and phone widths;
pnpm --dir content/kelly-portfolio-health-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 contact an external system, move money, or change
contract terms from the AirApp.
1---2name: kelly-portfolio-health3description: Busabase-backed App-in-Skill dashboard for a revenue-based-financing (RBF) fund or private-credit book of many small SME contracts. Use when the user invokes $kelly-portfolio-health or /kelly-portfolio-health, wants to check portfolio health, AUM, repayment progress, concentration risk, or a watchlist of contracts with declining revenue. Human actions (flag a contract for review, clear a flag, leave a note) write directly onto the contract's own Busabase record — this skill never moves money or changes contract terms. Generic and brand-free — not tied to any specific company or fund.4---56# RBF Portfolio Health Dashboard78## Overview910Kelly Portfolio Health is a Busabase Cloud App-in-Skill. Its canonical11product surface is the AirApp in Busabase, not a separate local-data12product. The same Hono source supports an explicitly requested local preview13with OAuth connection bootstrap. It gives a fund/credit-desk operator a14read-mostly dashboard over a revenue-share / private-credit book: many small15SME (small/medium enterprise) contracts, each an advance repaid as a share16of the SME's future revenue up to a cap. The app aggregates the book into a17top-line health summary, a repayment-progress-vs-time-elapsed view, an18industry/city concentration breakdown, and a watchlist of contracts with a19recent revenue decline. The only human action is lightweight: flag a20contract for review, clear a flag, or leave a note — everything else is a21read view.2223This is deliberately **generic and brand-free**: no real company, fund, or24SME name appears anywhere in the code, config, or seed data.2526This is a direct-manipulation dashboard, not a review-then-approve queue:27there is no AI-authored draft to approve and no separate execute/decisions28step. Totals, repayment lag, concentration, and the watchlist are computed29by a documented, deterministic function30(`content/kelly-portfolio-health-app/app/js/portfolio-model.js`, ported from the retired31`content/kelly-portfolio-health-app/server/insights.ts`); the human flags/clears/annotates a contract32directly in the UI, writing straight onto the contract's own Busabase record33— the same way `kelly-llm-gateway`'s rollout promote/rollback/hold and34`kelly-lead-funnel`'s kanban stage moves work.3536Default behavior is AirApp-first. Unless the user explicitly asks only for37explanation, give the user the clickable AirApp URL. Start localhost only38when local preview/debugging is explicitly requested; it uses the same39Busabase resources and never offers another data provider. Use chat-only40mode only when the user says "纯聊天", "chat only", "不要打开 UI", or similar.4142## Mandatory Dependencies43441. Read and follow `$kelly-app-skill-creator` for product behavior, visual45 quality, responsive layout, and the complete canonical `content/kelly-portfolio-health-app/` artifact.462. Read and follow `$busabase` for connection, target Space, node discovery,47 ChangeRequests, review, and merge behavior.483. Read and follow `$busabase-app-creator` for resource modeling, AirApp49 runtime limits, security, validation, and deployment.5051If a dependency is unavailable, preserve this skill's artifact and product52contracts, stop before the unavailable Busabase operation, and report the53exact missing dependency. Do not invent a second data backend.5455## App UI Screenshots5657<table>58 <tr>59 <td width="50%"><img src="assets/screenshots/overview.webp" alt="Portfolio health overview"></td>60 <td width="50%"><img src="assets/screenshots/concentration.webp" alt="Portfolio concentration"></td>61 </tr>62 <tr>63 <td><strong>Overview</strong><br>Total AUM, total collected, weighted-average repayment progress, at-risk count, category allocation, and the contracts most lagging behind their expected repayment pace.</td>64 <td><strong>Concentration</strong><br>Industry/category and city concentration by funding amount and contract count.</td>65 </tr>66 <tr>67 <td colspan="2"><img src="assets/screenshots/watchlist.webp" alt="Portfolio watchlist" width="50%"></td>68 </tr>69 <tr>70 <td colspan="2"><strong>Watchlist</strong><br>Contracts whose most recent month's revenue dropped materially below their trailing average, with a revenue sparkline and a flag-for-review / clear-flag / note action.</td>71 </tr>72</table>7374## Boundary7576- Read-mostly aggregation and human review flags only. NEVER contact any77 external system, brokerage, payment processor, or SME directly. NEVER move78 money, disburse funds, or change contract terms. There is no transaction79 path in this skill by design.80- The AirApp reads and writes its own Busabase Bases only; it never calls a81 live portfolio-servicing API. There is no execution/merge step beyond the82 direct write itself — a human still applies the decision in the real83 system of record.84- Contracts are never created by the AirApp; they enter Busabase through an85 external portfolio-sync process, the same way `kelly-llm-gateway`'s routes86 and `kelly-lead-funnel`'s leads enter through an upstream process the app87 doesn't control. The AirApp only ever updates an existing contract's88 `flagged`/`note`/`decision-updated-at` fields.89- Generic, brand-neutral tool: never hardcode or reference a specific real90 company, fund, or SME name in code, templates, or docs.91- Treat contract-level revenue and repayment data as sensitive. Never commit92 a local credential file or Busabase secrets.9394## Busabase Resources9596Two Bases under one application Folder (`kelly-portfolio-health`), declared97in `content/kelly-portfolio-health-app/app/js/config.js` and the generated template sidecars under `content/`:9899- `contracts`: one row per RBF/private-credit contract — funding terms100 (`funding-amount`, `cap-multiple`, `cap-amount`), `cumulative-repayment`, a101 6-month `monthly-revenue` series (JSON array), `status`, and the human102 review `flagged`/`note`/`decision-updated-at` fields written directly onto103 the same row. Totals, repayment lag, concentration, and the104 revenue-decline watchlist are never stored — they are pure/derived from105 these rows and recomputed on every read.106- `settings`: sanitized config (fund name, base currency, risk-policy107 thresholds), one row keyed by `kind`.108109Resources provision lazily through an idempotent Busabase ChangeRequest the110first time the app runs in a Space; see `references/portfolio-schema.md` for111exact field shapes.112113## Portfolio Health Model114115`content/kelly-portfolio-health-app/app/js/portfolio-model.js` (`computeInsights`) is ported verbatim from116the retired `content/kelly-portfolio-health-app/server/insights.ts`:117118- **Repayment lag** — per contract, `expected_pct` (months elapsed / term)119 vs. `actual_pct` (collected / cap); `lag_pp = expected_pct - actual_pct`,120 with a `severity` of `ok | watch | high` driven by121 `risk_policy.lag_watch_pp` / `lag_high_pp` (defaults 15 / 25 percentage122 points).123- **Concentration** — funding-amount concentration by category and by city,124 as a percentage of active-contract AUM.125- **Watchlist** — a contract qualifies when its most recent month's revenue126 is at least `risk_policy.revenue_decline_pct` (default 10%) below the127 average of its prior months (minimum 4 months of history).128- **At-risk count** — any contract with a non-`ok` lag severity, or129 `status: delinquent`.130131No randomness, no ML — the same snapshot always produces the same insights.132133## Direct Contract Decision134135The human action writes straight onto the contract's own Busabase record136through `busabase-sdk`, exactly like `kelly-llm-gateway`'s rollout writes —137there is no approval queue and no separate decisions bucket:138139- **Flag for review** / **Clear flag**: toggles `flagged` (`"true"`/`"false"`).140- **Save note**: sets `note` (free text).141142Both stamp `decision-updated-at` with the current time. From a standalone143local preview the write merges immediately (trusted operator); from the144deployed AirApp it creates a pending ChangeRequest for the trusted process145to merge, per the AirApp boundary in `$busabase-app-creator`.146147## Demo Mode148149- `?demo=1` opens a deterministic, fully offline mock portfolio (~52150 contracts across 8 categories and 10 cities) with computed insights, for151 documentation and screenshots. It never reads or writes Busabase and never152 claims a real connection; demo flag/note actions only update the153 in-memory snapshot already rendered in the browser tab.154- `?demo=overview`, `?demo=concentration`, `?demo=watchlist` select named155 demo scenes/routes.156- `lang=en` or `lang=zh` forces UI chrome language for screenshots.157158UI language: support English and Chinese chrome with `Auto` default.159160## Local App161162Default behavior is AirApp-first — give the user the clickable AirApp URL.163Start `pnpm --dir content/kelly-portfolio-health-app dev` only when local preview/debugging is explicitly164requested.165166## Views167168- `#/overview`: total AUM, total collected, weighted-average repayment169 progress, at-risk count, category allocation donut, and the contracts most170 lagging their expected repayment pace.171- `#/contracts`: sortable table (business, category, city, funding amount,172 actual progress, lag, status).173- `#/contracts/<id>`: per-contract detail — funding/cap/collected, expected174 vs. actual progress, a revenue sparkline, and the flag/note action.175- `#/concentration`: funding-amount concentration by category and city.176- `#/watchlist`: contracts with a recent revenue decline, each with a177 sparkline and a flag-for-review / clear-flag action.178- `#/settings`: sanitized configuration summary — data provider, fund name,179 base currency, and risk-policy thresholds. Never expose secret values.180181## Completion Criteria182183Finish only when:184185- the skill contains the complete canonical `content/kelly-portfolio-health-app/` project and186 `pnpm --dir content/kelly-portfolio-health-app dev` remains supported;187- all persistent config, state, and domain data use `busabase-sdk` and the188 declared resource map — no local JSON, browser storage, or provider189 choice;190- Vault values and API credentials never reach browser-visible surfaces;191- local setup offers Cloud/custom URL OAuth plus the explicit Demo path,192 while a deployed AirApp uses its ambient session;193- Overview, Contracts, Concentration, Watchlist, and Help & Settings render194 on desktop and phone widths;195- `pnpm --dir content/kelly-portfolio-health-app run check` and `node --test` pass.196197## Stop Conditions198199Stop before consequential Busabase mutation when the target Space is200ambiguous, the current user lacks permission, or a same-slug resource is not201application-owned. Never contact an external system, move money, or change202contract terms from the AirApp.