附录 · 投资人 Referral 激励
这一页是给机器跑的,你不用看。要看给人看的那份,回正文 PRD。
- 版本 Version: v1.0 · 2026-08-30 · owner: session
dreamy-poitras-ee4c95(分支 claude/dreamy-poitras-ee4c95)
- 正文那张功能表只放人话摘要,这一页放完整可跑版本:文件路径、命令行、内部字段名、来源编号。
- 本 PRD 不在 website 仓库里新建任何代码文件——它是一份判定规则 + 合规红线的规范文档,检查读的是已经存在于其他仓库的文件(
entry.py / Referral_Incentive_Plan_2026-08-29.html)以及这份 PRD 自己。
功能 → 检查(完整版)
| # | 涉及的既有文件 | 可跑的检查 |
| F1 |
/Users/jack/yummy-repos/wefunder-pitch/.claude/worktrees/exciting-bouman-a25665/campaign-comms/insider-ladder/entry.py——本 PRD 引用它已经定义好的 referral_pending/referral_confirmed 两态,不重新定义 |
node 读 entry.py 源码,断言「待确认」「已确认」这两个状态字符串(详见下方命令)字面存在,证明本 PRD 引用的状态名不是编造的。命令见下方 CHECK-F1。 |
| F2 |
同上 entry.py——第 37 行 ENGAGEMENT_HEADER = ["email", "date", "type", "note"] 本身就没有金额字段 |
node 读 entry.py 源码,断言 ENGAGEMENT_HEADER 那一行不包含 amount,且全文不出现把 referral 和 amount 绑在一起的字样,证明记账层没有金额维度。命令见下方 CHECK-F2。 |
| F3 |
/Users/jack/yummy-repos/website/.claude/worktrees/dreamy-poitras-ee4c95/docs/prd/investor-referral-incentive.html(本 PRD 正文自己) |
node 读正文 html,抓出所有 <h1>/<h2>/<h3> 标题文字,断言没有一个标题命中"决定/决策/Decisions"。命令见下方 CHECK-F3。 |
| F4 |
/Users/jack/yummy-repos/wefunder-pitch/.claude/worktrees/sad-bhabha-64e893/campaign-comms/Referral_Incentive_Plan_2026-08-29.html(§05 永久出局清单原文)+ 本 PRD 正文自己 |
node 读两份文件,对源文档 §05 的 4 条永久出局分别取关键词组,逐组断言至少一个关键词能在本 PRD 正文的合规红线小节里命中,证明复述没有漏项。命令见下方 CHECK-F4。 |
CHECK-F1 · 状态名不是编造的
node -e "
const fs = require('fs');
const p = '/Users/jack/yummy-repos/wefunder-pitch/.claude/worktrees/exciting-bouman-a25665/campaign-comms/insider-ladder/entry.py';
const src = fs.readFileSync(p, 'utf8');
console.assert(src.includes('referral_pending'), 'FAIL: referral_pending 不存在于 entry.py,本 PRD 引用的状态名是编造的');
console.assert(src.includes('referral_confirmed'), 'FAIL: referral_confirmed 不存在于 entry.py,本 PRD 引用的状态名是编造的');
console.log('PASS: referral_pending / referral_confirmed 两个状态名在 entry.py 里都真实存在');
"
CHECK-F2 · 记账字段里没有金额维度
node -e "
const fs = require('fs');
const p = '/Users/jack/yummy-repos/wefunder-pitch/.claude/worktrees/exciting-bouman-a25665/campaign-comms/insider-ladder/entry.py';
const src = fs.readFileSync(p, 'utf8');
const headerLine = src.split('\n').find(l => l.includes('ENGAGEMENT_HEADER ='));
console.assert(!!headerLine, 'FAIL: 没找到 ENGAGEMENT_HEADER 定义行,entry.py 可能已经改版,需要重新核对');
console.assert(headerLine && !/amount/i.test(headerLine), 'FAIL: ENGAGEMENT_HEADER 里出现了 amount 字段,记账层已经在按金额记账,跟合规红线第 2 条矛盾');
console.assert(!/referral[\s\S]{0,40}amount|amount[\s\S]{0,40}referral/i.test(src), 'FAIL: entry.py 出现了把 referral 和 amount 绑在一起的字样,需要重新核对分档是否真的只看人数');
console.log('PASS: entry.py 的记账字段(' + headerLine.trim() + ')里没有金额维度');
"
CHECK-F3 · 正文没有"决定"类标题
node -e "
const fs = require('fs');
const p = '/Users/jack/yummy-repos/website/.claude/worktrees/dreamy-poitras-ee4c95/docs/prd/investor-referral-incentive.html';
const src = fs.readFileSync(p, 'utf8');
const headings = [...src.matchAll(/<h[123][^>]*>([\s\S]*?)<\/h[123]>/g)].map(m => m[1].replace(/<[^>]+>/g, ''));
const banned = headings.filter(h => /决定|决策|Decisions?/i.test(h));
console.assert(banned.length === 0, 'FAIL: 出现了禁用标题:' + JSON.stringify(banned));
console.log('PASS: 正文没有任何标题叫决定/决策/Decisions,共检查 ' + headings.length + ' 个标题');
"
(上面这段命令里的 </> 是本页 HTML 转义显示,真正贴进终端跑时用普通的尖括号 </>。)
CHECK-F4 · 合规红线复述不漏项
node -e "
const fs = require('fs');
const planPath = '/Users/jack/yummy-repos/wefunder-pitch/.claude/worktrees/sad-bhabha-64e893/campaign-comms/Referral_Incentive_Plan_2026-08-29.html';
const docPath = '/Users/jack/yummy-repos/website/.claude/worktrees/dreamy-poitras-ee4c95/docs/prd/investor-referral-incentive.html';
const plan = fs.readFileSync(planPath, 'utf8');
const doc = fs.readFileSync(docPath, 'utf8');
console.assert(/永久出局/.test(plan), 'FAIL: 源文档 §05 永久出局清单标题都找不到了,来源可能已经改版,需要重新核对');
const groups = [
['现金', '返现', '礼品卡', '折扣券'],
['按被带者投资额分档', '按.{0,6}投资额分档', '挂钩金额'],
['股权', 'SAFE', '未来轮次优先权'],
['排行榜', '带人竞赛']
];
groups.forEach((group, i) => {
const hit = group.some(k => new RegExp(k).test(doc));
console.assert(hit, 'FAIL: 第 ' + (i + 1) + ' 条永久出局的关键词一个都没有在本 PRD 合规红线小节里出现,复述漏了一条');
});
console.log('PASS: 4 条永久出局的关键词在本 PRD 正文里都能找到至少一个命中');
"
(同上,尖括号转义显示,贴进终端时用普通尖括号。)
现状 vs 未拍板:来源一览
| 事项 | 状态 | 来源 |
| 不用现金,走代表身份的东西 | 已拍板(Q99) | 决策台账 Q99;本 PRD 合规红线小节承接 |
| 三档纪念品阶梯:第 1 人豆袋标 / 往上纪念币 / 封顶围裙 + 墙砖 | 已拍板(Q103,整批 go) | 决策台账 Q103 |
| 确认成交只按人数分档,不看金额 | 已拍板(Referral 方案 §00 原则②) | /Users/jack/yummy-repos/wefunder-pitch/.claude/worktrees/sad-bhabha-64e893/campaign-comms/Referral_Incentive_Plan_2026-08-29.html §00 |
| 11 条新点子选哪几条 | 未拍板,批注中 | /Users/jack/yummy-repos/wefunder-pitch/.claude/worktrees/serene-pike-c2b0b5/campaign-comms/Referral_Ideas_2026-08-30.html |
| A/B/C/D 四套完整方案选哪套(源文档称 DB1) | 未拍板,源文档 §06 明写"板上等 Jack 点" | /Users/jack/yummy-repos/wefunder-pitch/.claude/worktrees/sad-bhabha-64e893/campaign-comms/Referral_Incentive_Plan_2026-08-29.html §06 |
记账两态模型(只引用,来源 entry.py,不改一行代码)
entry.py(/Users/jack/yummy-repos/wefunder-pitch/.claude/worktrees/exciting-bouman-a25665/campaign-comms/insider-ladder/entry.py)第 12-40 行注释、第 152-208 行函数定义了整套双向确认逻辑,本 PRD 只引用结论,不重新定义触发条件或存储格式:
- 第 1 步:带人者自报或被带者回信提到介绍人,调用
start_referral()(150 行附近)追加一行 referral_pending 到互动 CSV(append-only,不改不删旧行),同时在 sidecar 文件 <csv>.referrals.json 记一条待双向确认的状态,带 confirmed_referrer/confirmed_referred 两个布尔位。
- 第 2 步:
confirm_referral_side()(178 行附近)分别给推荐人、被推荐人各自打勾,两边独立确认。
- 第 3 步:两边都打勾后,
upgrade_referral()(190 行附近)才允许把状态升级为 referral_confirmed,写一行新记录(旧的 pending 行不改不删),且已经升级过的 key 再点一次不会重复写行(幂等)。
- 全程没有任何字段记录被带者投资额——
ALLOWED_TYPES(38-40 行)和 ENGAGEMENT_HEADER(37 行)都不含金额字段;referral_confirmed 故意不放进下拉框选项,只能靠双向确认升级产生,不给人手填。
接线约定
- 本 PRD 不新建任何后端接口或前端代码——它的唯一产出是一套人数判定规则 + 一份合规红线文字规范,供邮件文案和记账操作规则参照。
- 记账机制的读写实现完全在
wefunder-pitch 仓库的 campaign-comms/insider-ladder/entry.py,本 PRD 不改那边一行代码,只引用它已经定义好的两个状态名。
- 与主 PRD
investor-perk-journey.html 之间没有代码耦合——这条奖励要不要接进 Perks 页签视觉是主 PRD 未来的决定,接的话会由主 PRD 另开功能项,不是这份文档的产出。