private-data-boundary
The owner's private investment data and where it may never go. Invested amounts, invest dates, realized/unrealized/current value, [redacted]/TVPI/DPI, per-company or per-deal [redacted] (one general portfolio [redacted] is allowed, from data/portfolio-performance.yaml), carry, fund or entity names, share class, [redacted]-or-cap, round terms, and links to his AngelList portfolio must never be in a committed file, a build, a deploy target or another repo. The one exception is angel-investments-performance, which is always kept. Use before any data, Airtable, AngelList, sync, import, deploy, cleanup or cross-repo work, and whenever a file or page might carry deal data.
- Source file
.claude/skills/private-data-boundary/SKILL.md
Private data boundary
The rule
The Airtable Manus view's columns are fixed. Only its public columns
(data/airtable-manus-sync.json publicFields: name, market, exit status,
latest market cap, one-liner, website, acquired by, category, sub-category)
may be written to any file, in any repo, or to any deploy target.
Never write, and never pull from AngelList or another Airtable view:
- invested amounts and invest dates
- realized, unrealized and current value
- [redacted], TVPI, DPI, RVPI
- per-company or per-deal IRR, and any second IRR figure (see below)
- carry, fund names, investment entities
- share class, valuation-or-cap, [redacted] and other terms
No AngelList links (owner, 2026-10-11): no angellistUrl key and no value,
logo included, that links to his AngelList portfolio. Airtable's "Angellist
URL" column is never read.
IRR: one general figure only
Owner, 2026-10-11: "IRR is ok, general IRR is ok, only one IRR is allowed,
which is general." The portfolio-wide IRR may appear on the public site as one
figure. It lives in exactly one place, data/portfolio-performance.yaml
(generalIrr), read through shared/portfolioPerformance.ts; copy in
content/ and page code uses the {irr} placeholder and the label "IRR".
Never write the number itself anywhere else, never a per-company or per-deal
IRR, and never a second IRR figure (industry benchmarks included).
server/general-irr-single-source.test.ts fails on a hard-coded percentage
next to "IRR" or the literal figure outside the source file. The llms files and
data downloads still carry no IRR at all (RESTRICTED_PERFORMANCE_TERMS in
server/publicContentPolicy.ts).
The one exception
Always keep angel-investments-performance (owner, 2026-10-11). It is the
private Angel Simulator (~/Code/sahin/angel-investments-performance, private
repo sahin/angel-investments-performance) and holds this data on purpose.
Never delete or archive it, never rewrite its history, never scrub its data,
including during privacy cleanups. A user-level PreToolUse hook
(~/.claude/hooks/keep-angel-investments-performance.sh) blocks those commands.
Enforcement
server/private-deal-data.test.ts: fails on any deal field in a committed JSON file, any AngelList portfolio link in any committed file, and any of the retired private exports coming back.pnpm privacy:bundle(scripts/validate-public-bundle.mjs): the same checks on every shipped file indist/cloudflare.- Field list and patterns:
scripts/private-deal-fields.mjs. Never loosen them. server/general-irr-single-source.test.ts: the general IRR figure exists only indata/portfolio-performance.yaml; no second IRR figure anywhere.- Agent:
private-data-auditorsweeps the repo, build, live site, sibling repos and Cloudflare Pages projects (read-only).
Before data, sync, deploy or cleanup work
Check every place the data lands, not just this repo:
- sibling repos under
~/Code/sahin/(angel-investments,web-angel-investments-*), but leaveangel-investments-performancealone - every Cloudflare Pages project in account
9ea45491…. Old projects keep serving files, and every deployment keeps its own URL. - Cloudflare caches hashed assets for a year (
immutable). A file removed from the build can stay reachable until it is purged.
When a shipped bundle carried private data
- Remove the data at its source and rebuild (the bundle gets a new hash).
- Add the old path to
REVOKED_PUBLIC_ASSETSinserver/publicRouting.ts. The worker then answers it with a 404 before Cloudflare's asset cache is consulted.server/cloudflare-worker.test.tsproves this. - Ship it. Every deploy runs
scripts/cloudflare-purge.ts, which purges each revoked bundle from both sahin.io cache layers and checks it answers 404. If one is still served, it purges the whole zone and checks again. The run fails if that doesn't work, or if theCLOUDFLARE_API_TOKENsecret lacks the Zone > Cache Purge permission on sahin.io. - Older deployments keep their own pages.dev URLs and old worker. Only deleting those deployments removes the file there.
History
On 2026-10-11 an old Manus dashboard (angel-portfolio-dashboard.pages.dev,
repo sahin/angel-investments) was found serving these fields. The
project was deleted and the files purged from that repo's history.
portfolio-data-airtable.json, scripts/airtable-raw-export.json and 285
AngelList links were removed from this repo (6e74d0e, ad0d60c).