70 added, 64 removed. Audit A to A.
---
name: kelly-family-office
- description: Family office App-in-Skill that consolidates multi-entity investment holdings into one read-only dashboard. Use when the user invokes $kelly-family-office or /kelly-family-office, wants a consolidated family office view, multi-entity aggregation across an individual, trust, company, fund, or foundation, total AUM in a base currency, asset allocation by asset class, by entity/member, or by custodian/institution, multi-currency FX consolidation, CSV import of holdings, manual holdings entry, unrealized P/L, or read-only portfolio monitoring. It never moves money or trades.
+ description: Busabase-backed family-office App-in-Skill that consolidates multi-entity investment holdings into one read-only dashboard. Use when the user invokes $kelly-family-office or /kelly-family-office, wants a consolidated family office view, multi-entity aggregation across an individual, trust, company, fund, or foundation, total AUM in a base currency, asset allocation by asset class, by entity/member, or by custodian/institution, multi-currency FX consolidation, CSV import of holdings, unrealized P/L, or read-only portfolio monitoring. It reads Busabase only and NEVER moves money or trades.
---
# Kelly Family Office
## Overview
- Use this skill as Kelly's local family-office aggregation desk. It consolidates the holdings of multiple entities and members — an individual, a family trust, an offshore company, a fund, a foundation — into one file-backed App-in-Skill dashboard: total AUM in a base currency, unrealized P/L, and allocation by entity, asset class, and institution. Data comes from CSV import and manual holdings entry; there is no live brokerage API in v1.
+ Kelly Family Office 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. Use this skill as Kelly's family-office aggregation desk. It consolidates the holdings of multiple entities and members — an individual, a family trust, an offshore company, a fund, a foundation — into one dashboard: total AUM in a base currency, unrealized P/L, and allocation by entity, asset class, and institution. Data comes from CSV import (through a trusted skill-root script); there is no live brokerage API in v1.
- Default interaction mode: App UI. Unless the user explicitly asks for chat-only handling, check onboarding/config, refresh or load the local holdings snapshot, start/reuse the local app with `app/start.sh`, and give the actual local URL. Use chat-only mode only when the user says "纯聊天", "chat only", "不要打开 UI", or similar.
+ 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 local 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="Kelly Family Office overview"></td>
<td width="50%"><img src="assets/screenshots/entities.webp" alt="Kelly Family Office by entity"></td>
</tr>
<tr>
<td><strong>Overview</strong><br>Consolidated command desk with total AUM in the base currency, unrealized P/L, entity and account counts, and headline allocation.</td>
<td><strong>By entity / member</strong><br>Each family entity (individual, trust, company) with its consolidated AUM, portfolio weight, and unrealized P/L.</td>
</tr>
<tr>
<td width="50%"><img src="assets/screenshots/assets.webp" alt="Kelly Family Office by asset class"></td>
<td width="50%"><img src="assets/screenshots/institutions.webp" alt="Kelly Family Office by institution"></td>
</tr>
<tr>
<td><strong>By asset class</strong><br>Allocation across equity, bond, cash, crypto, real estate, private equity, and alternatives, with a donut, weighted bars, and a value table.</td>
<td><strong>By account / institution</strong><br>Consolidation by custodian and institution to see where assets are held and concentration across banks and brokers.</td>
</tr>
<tr>
<td width="50%"><img src="assets/screenshots/performance.webp" alt="Kelly Family Office performance"></td>
</tr>
<tr>
<td><strong>Performance</strong><br>Cost basis versus market value and unrealized P/L, per entity and for the whole family office, in the base currency.</td>
</tr>
</table>
## Boundary
- - The skill may read holdings CSV exports, normalize them, validate schemas, and write local snapshot files.
- - The app reads and writes local files only. It must NOT connect to any brokerage/custody API, move money, place trades, rebalance, transfer, or mutate any remote system. This is a read-only monitoring dashboard.
- - Treat all holdings and account data as sensitive. Do not commit `config.local.json`, env files, `app/.data/`, raw CSV exports, statements, or account identifiers.
- - There is no approval lifecycle and no decisions file — this is monitoring only.
-
- ## First Run And Onboarding
+ - The skill may read a holdings CSV, normalize it, and write entity/account/holding rows into Busabase through the trusted `scripts/import_csv.mjs` process.
+ - The AirApp reads Busabase records only; it is entirely read-only and must NEVER connect to a brokerage/custody API, move money, place trades, rebalance, transfer, or mutate any remote system. It is a read-only monitoring dashboard (`readOnly: true`, no `writeProcedures`).
+ - Treat all holdings and account data as sensitive. Never commit raw CSV exports, statements, account identifiers, Busabase credentials, or env files.
+ - There is no approval lifecycle and no decisions workflow — this is monitoring only.
- On invocation, check `app/.data/onboarding.json` and private config readiness. If onboarding is absent/incomplete, guide setup before importing real holdings.
+ ## Busabase Resources
- Set up in this order:
+ Four Bases under one application Folder (`kelly-family-office`), declared in
+ `app/app/js/config.js` and `app/resource-map.json`:
- 1. Define entities (`entity_id`, `name`, `type` one of INDIVIDUAL/TRUST/COMPANY/FUND/FOUNDATION, `member`).
- 2. Define institutions and the `base_currency`.
- 3. Set `fx_rates` for every non-base currency you hold (value in base currency; base = 1).
- 4. Import a holdings CSV or maintain holdings manually, then start the app.
+ - `entities`: the individuals, trusts, companies, funds, and foundations being consolidated (`entity_id`, `name`, `type`, `member`).
+ - `accounts`: custodian/institution accounts held by each entity (`account_id`, `entity_id`, `institution`, `account_type`, `currency`, `display_name`, `as_of`). Institutions are just a field on accounts — there is no separate Institutions Base.
+ - `holdings`: individual holdings across every account (`holding_id`, `entity_id`, `account_id`, `symbol`, `name`, `asset_class`, `quantity`, `cost_basis`, `market_value`, `currency`, `as_of`).
+ - `settings`: one row per `kind` — `office-meta` (`base_currency`, `fx_rates`, `target_allocation`, non-secret) and `onboarding`.
- Private config priority:
+ 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. The AirApp never writes to any Base — only the trusted
+ CSV importer does. The consolidated snapshot (totals, by_entity,
+ by_asset_class, by_institution, insights) is computed client-side from
+ `entities`/`accounts`/`holdings`/`settings` on every read — it is never
+ stored.
- 1. `KELLY_FAMILY_OFFICE_CONFIG=/absolute/path/to/config.json`
- 2. `skills/kelly-family-office/config.local.json`
- 3. `~/.config/kelly-family-office/config.json`
- 4. `skills/kelly-family-office/config.example.json` as template only
+ ## First Run And Onboarding
- Env priority:
+ On invocation, check the `office-meta` and `onboarding` settings rows for
+ readiness. If absent, guide setup before importing real holdings.
- 1. Existing environment variables
- 2. `KELLY_FAMILY_OFFICE_ENV_FILE=/absolute/path/to/.env`
- 3. Repository root `.env`
- 4. `skills/kelly-family-office/.env.local`
- 5. `~/.config/kelly-family-office/.env`
+ Set up in this order, asking only for non-secret setup details:
- Ask only for non-secret setup details: entity names/types, members, institutions, base currency, and FX rates. When setup is complete and the user confirms, write `app/.data/onboarding.json`:
+ 1. Define entities (`entity_id`, `name`, `type` one of INDIVIDUAL/TRUST/COMPANY/FUND/FOUNDATION, `member`).
+ 2. Define the `base_currency` (default USD).
+ 3. Set `fx_rates` for every non-base currency you hold (value in base currency; base = 1).
+ 4. Optionally set `target_allocation` per asset class (used by the `allocation_drift` insight).
+ 5. Import a holdings CSV via `scripts/import_csv.mjs`.
- ```json
- {
- "completed": true,
- "completed_at": "ISO timestamp",
- "config_version": "1"
- }
- ```
+ Write the `office-meta` and `onboarding` settings rows through the trusted
+ process; never ask the user to paste secrets into chat.
- ## CSV Format
+ ## CSV Import
- Import holdings with `scripts/import_csv.ts`. The documented template is `references/holdings-csv-template.csv`. Columns:
+ Kelly Family Office's AirApp is read-only; `scripts/import_csv.mjs` is the
+ only process that writes holdings rows. The documented template is
+ `references/holdings-csv-template.csv`. Columns:
- `entity_id`, `entity_name`, `entity_type` (INDIVIDUAL|TRUST|COMPANY|FUND|FOUNDATION), `member`
- `account_id`, `institution`, `account_type`, `account_currency`
- `holding_id`, `symbol`, `name`, `asset_class` (EQUITY|BOND|CASH|CRYPTO|REAL_ESTATE|PRIVATE_EQUITY|ALTERNATIVE)
- `quantity`, `cost_basis`, `market_value` (totals in the holding `currency`), `currency`, `as_of`
Run:
```bash
- node scripts/import_csv.ts path/to/holdings.csv
+ BUSABASE_BASE_URL=... BUSABASE_API_KEY=... BUSABASE_SPACE_ID=... \
+ node scripts/import_csv.mjs path/to/holdings.csv --apply
```
- It normalizes rows into `app/.data/snapshot.json`, converts each holding to the base currency via config `fx_rates`, computes the `totals` and the `by_entity` / `by_asset_class` / `by_institution` rollups, and writes `app/.data/import_report.json`. Manual entry is the same file — maintain the CSV (or a holdings JSON matching `references/portfolio-schema.md`) and re-run the importer.
+ Without `--apply` it is a dry run that only prints the planned writes. It
+ resolves entity/account references against Busabase, creating a new entity
+ or account record on the fly if the CSV names one that doesn't exist yet,
+ then writes `holdings` rows via Busabase ChangeRequests.
## Demo Mode
- `?demo=1` (or `?demo=overview`) opens a deterministic offline family office: 3 entities (individual, trust, offshore company), 6 accounts across Interactive Brokers, HSBC, UBS, and Coinbase Custody, and ~16 multi-currency holdings (USD/HKD/CNY) consolidated to a USD base.
- `?demo=entities`, `?demo=assets`, `?demo=institutions`, `?demo=performance`, and `?demo=detail` select named scenes.
- - `lang=en` or `lang=zh` forces UI chrome language for screenshots.
- - Demo API responses never read or write live data or local private holdings files.
-
- ## Data Provider Seam
-
- The Hono app reaches storage only through the logic modules, so the same `app.fetch` deploys to Cloudflare Workers once the data layer moves to a cloud provider. Select the backend with `KELLY_FAMILY_OFFICE_DATA_PROVIDER=local` (default `local`).
+ - `lang=en` or `lang=zh` forces UI chrome language (and localizes the demo dataset's entity/holding names) for screenshots.
+ - Demo mode never reads or writes Busabase.
## Local App
- Start the dashboard with:
-
- ```bash
- skills/kelly-family-office/app/start.sh
- ```
-
- The app uses local HTTP on `127.0.0.1`, preferring port `3000` through `4000`, or `KELLY_FAMILY_OFFICE_UI_PORT` when set. UI language supports English and Chinese chrome with an `Auto` default.
+ 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. UI language supports English and Chinese chrome with an `Auto`
+ default.
## Views
- `#/overview`: consolidated total AUM (base ccy), unrealized P/L, entity count, and a headline allocation donut.
- `#/entities`: entity/member sidebar; `#/entities/<entity_id>` drills into that entity's accounts, holdings, and subtotal.
- `#/assets`: asset-class allocation donut + bars + table with weights.
- `#/institutions`: consolidated by custodian/institution.
- `#/performance`: cost vs market value and unrealized P/L (absolute + %), per entity and total.
- - `#/settings`: sanitized setup summary (base currency, FX rates, entities, institutions, data provider, onboarding state, config path). Never expose secrets.
-
- ## File Contract
+ - `#/settings`: sanitized setup summary (base currency, FX rates, entities, institutions, data provider, onboarding state). Never expose secrets.
- Read `references/portfolio-schema.md` before editing the app, scripts, or any generated snapshot JSON.
+ ## Insights
- Primary local files:
+ Read-only, deterministic observations rendered from `{ code, severity, params }` by localized templates (en + zh). Codes: `asset_class_concentration`, `institution_concentration`, `entity_concentration`, `allocation_drift`, `currency_exposure`, `cash_level`. They are neutral facts, never advice, and never actions.
- - `app/.data/snapshot.json`: canonical dashboard snapshot generated by the importer/scripts.
- - `app/.data/onboarding.json`: onboarding completion marker.
- - `app/.data/import_report.json`: latest CSV import result.
- - `config.local.json`: private entities/institutions/FX config, ignored by git.
+ ## File Contract
- Use `node scripts/validate_ui_schema.ts app/.data/snapshot.json` before relying on a snapshot in the UI. It checks entity/account/holding references, enum values, that `aum_base` equals the sum of holdings' base market values, and that each rollup's weights sum to ~100%. The app shows an empty "Needs holdings import" state when no snapshot exists.
+ Read `references/portfolio-schema.md` before editing the app, `app/app/js/config.js`,
+ or `scripts/import_csv.mjs`.
## Safety Defaults
- - Never connect to a live brokerage/custody API, trade, transfer, or rebalance. This skill only reads local files and renders them.
- - Keep raw statements and exports outside git; store only normalized safe fields in the snapshot.
+ - Never connect to a live brokerage/custody API, trade, transfer, or rebalance. The AirApp only reads Busabase and renders it; only the trusted importer writes.
+ - Keep raw statements and exports outside git; write only normalized safe fields to Busabase.
- If a holding is unpriced or a currency is missing an FX rate, mark it with a warning rather than inventing a value.
- - Keep FX rates explicit and dated; stale rates should surface as a warning, not silent drift.
+ - Keep FX rates explicit; stale rates should surface as a warning, not silent drift.