docs(api): trial 配额两个数字与真实 0.8.8 不符;一处行号 pin 已漂到无关代码 - #810
Conversation
## 1. trial limits 有两个数字是错的
文档 GET /api/license 的 trial 样例写:
"limits": { "max_agents": 5, "max_networks": 1, "max_tasks_day": 100 }
真实 0.8.8(干净容器 bunx --bun @sleep2agi/commhub-server@0.8.8,全新库,type=trial):
"limits": { "max_agents": 5, "max_networks": 3, "max_tasks_day": 500 }
max_networks 1→3、max_tasks_day 100→500。源码侧确认不是环境差异 ——
server/src/db.ts 的 licenses 表 schema 默认就是:
max_agents INTEGER DEFAULT 5
max_networks INTEGER DEFAULT 3
max_tasks_day INTEGER DEFAULT 500
而 /api/license 直接读这张表(server.ts:888)。
这条会影响用户决策:照文档看会以为试用期只能建 1 个网络、每天 100 个任务。
## 2. 同页一处行号 pin 已经漂了
第 396 行(en 398)说 plan quota "在 auth.ts:184-189 enforced"。
实测 auth.ts:184-189 现在是**发 user token** 的代码,与配额无关;
真正的关卡在 createNetwork() 里(QUOTAS 表 + max_networks_owned 判定)。
根因是钉行号 —— 行号必然漂。改成钉函数名。
(RELEASE-SOP 里 R367 对 sdk-deep-dive.md 做过同样的动作:取消行号 pin。)
原文里「free plan 默认 max_networks_owned=2」核过,是对的,保留。
## 范围
只改 docs-site 的 zh + en 两个文件,不动产品源码。
探测全部在容器里做,生产只做了只读 curl。
接受这条 MINOR —— 是我措辞过宽审查意见指出中文的「当前 quota 仍在 我的本意只覆盖建网门,但句子里 拟采纳的替换(来自审查意见,我未改动):
现在不改分支 —— 本 PR 在冻结中(我此前反复改动导致复审无法收口,已停手)。这条记在这里,等复审收口后随其它意见一并落。 另外确认一条 NOT COVERED 的定性:作者(我)那句 0.8.8 干净容器动态探测,审查方判定「仅作者声明、无独立日志、不得当独立复验证据」是对的。这里数字的正确性应当只由 base 的 |
自审(本 PR 至今零行内审查):把 5/3/500 这条链路从头验到尾,成立;顺带关掉一条 NOT COVERED本 PR 是三条从没被自动审查器碰过的最后一条(#823 已自审出一条 fail-open 并修,#800 自审为无发现)。 文档 PR 的生死线就是数字对不对,所以直接走源码。 完整链路(三跳,全部命中)① 建表默认值 — max_agents INTEGER DEFAULT 5,
max_networks INTEGER DEFAULT 3,
max_tasks_day INTEGER DEFAULT 500,② trial 插入不指定这三列 — INSERT INTO licenses (id, license_key, type, expires_at)
VALUES (?1, ?2, 'trial', datetime('now', '+14 days'))只有四列,三个配额列缺席 → 取①的默认值。 (对照: ③ REST 原样回传 — "SELECT type, expires_at, max_agents, max_networks, max_tasks_day FROM licenses ORDER BY created_at LIMIT 1"
…
limits: { max_agents: license.max_agents, max_networks: license.max_networks, max_tasks_day: license.max_tasks_day },逐字段透传,没有任何变换、覆盖或兜底。 结论文档里的 这关掉了一条 NOT COVERED此前的窄审留了这条:
现在 REST 响应路径这一支已验。 值得说清楚的是用什么验的:不是重跑我自己那次 0.8.8 动态探测 —— 审查方判定「仅作者声明、无原始日志,不得当独立复验证据」是对的,而我再跑一次仍然是同一作者侧,不会变得更独立。走源码链路才是能独立复现的那条路:任何人拿这三个行号就能自己走一遍。 仍未覆盖(不夸大)
|
最后一条 NOT COVERED 验完了,而结论比原来那条 MINOR 更重此前留的是:
验完:不但没 enforce,这三个数在整个仓里没有任何人 enforce。 ①
|
| 引用点 | 做什么 |
|---|---|
server/src/server.ts:899 |
塞进 /api/license 响应 |
agent-network/bin/cli.ts:12819 |
打印:Soft limits: agents=…, networks=…, tasks/day=… |
除此之外没有比较、没有拒绝、没有门。产品自己的 CLI 就把它们叫 Soft limits。
(扫描范围:origin/main 全仓 *.ts / *.tsx,排除 *.test.*。auth.ts 里那几处 max_networks 是另一个东西 —— plan QUOTAS 的 max_networks_owned,不是 licenses 表的列。)
所以原来那条 MINOR 说轻了
原判是「中文措辞过宽,易把 license trial 的 5/3/500 与 free plan 建网门 max_networks_owned=2 混成一层」。方向对,但实际情况更尖:
这两组数不只是"层不同",而是一组生效、一组根本不生效,而且:
- license 说
max_networks: 3,plan 门实际按max_networks_owned: 2拒绝 —— 两个数不一样,读者按3去建第 3 个网络会被拒; - 文档把
"limits": {5, 3, 500}摆在讲已生效配额门的段落旁边,读者几乎必然把它读成"这就是被 enforce 的限额"。
这跟文档自己已经点名的 max_members(dormant)是同一类,只是这三个还没被点名。
建议改法(比之前那版更准)
/api/license的limits(trial 默认max_agents=5/max_networks=3/max_tasks_day=500)是软限额:服务端只存储和返回,不做任何拦截(CLI 里也直接标作Soft limits)。真正会拒绝请求的是 plan 配额 ——auth.ts的createNetwork()按max_networks_owned校验(free=2,admin 豁免)。注意两者的 networks 数字不同(3 vs 2),以实际生效的 plan 配额为准。
冻结中未改分支,与此前接受的那条 MINOR 一并待收口后落。
至此本 PR 的 NOT COVERED 只剩一条:ORDER BY created_at LIMIT 1 取最早那行 license,文中数字描述的是全新安装状态。这条我认为是文档范围的自然边界,不打算再收窄。
同一页上还有 4 个行号 pin 也漂了 —— 本 PR 修了一个,没扫同类本 PR 的立意之一就是「原文钉的
真正的 其中一处特别值得指出来
这句话的全部作用就是「别信旧文档,去这两个坐标自己核」—— 而这两个坐标核不了任何东西。 一条指向失效的验证指引,比没有指引更糟:它让人以为已经可以核实。 建议按本 PR 已经确立的做法处理,不是更新数字,是改钉符号:
理由和本 PR 对 一条对我自己的批评本 PR 发现了「行号 pin 会漂」这个问题,却只修了触发它的那一处。修了一个实例、没扫同一类,而同页另外四处一直是坏的。 这跟我这两天反复在别人和自己代码里查的是同一件事:发现一个形态之后,必须查它是不是模式。我在 #815 的清单里写过这条,自己在这条 PR 上没做。 冻结中未改分支,与前两条(MINOR 措辞、软限额)一并待收口后落。 |
|
@codex review 本 PR 至今零审查记录(自动审查器只在开 PR / 草稿转就绪 / 显式 @ 时触发,push 不触发)。这是第二次请求。 |
|
Codex Review: Didn't find any major issues. Delightful! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
这三条我在窄审后都写过"我认",然后一直停在"冻结中,待收口后落"。 而"收口"这个条件实际上从没到来 —— 我自己设的延后规则,变成了阻塞 自己修复的东西。本 PR 至今零审查记录,根本不存在会被打断的在途复审。 ① 措辞过宽 + 软限额定性(两条合一处改): 原文"当前 quota 仍在 createNetwork() 里 enforced"会把两层混成一层。 实际更尖:/api/license 的 limits(trial 5/3/500)全仓无人 enforce —— 唯一去处是塞进响应、以及 CLI 打印成 "Soft limits";真正会拒绝建网的 是 plan 配额 max_networks_owned(free=2)。两者 networks 数字还不同 (3 vs 2),读者按 3 去建第 3 个网络会被拒。中英两侧都改。 ② 行号 pin:该页 tools.ts#L521 / #L286 / #L571 全部已漂(#L521 指到 daemon role 的 zod 注释,真正的 license_expired 在别处)。按 #831 对 参考页的结论,去掉误导的行号锚点 —— 链接指向文件是准的,而符号名 (license_expired / chained_reply / payload)本来就写在句子里。 中英各 3 处,共 6 处,改后残留 0。
三条一直停在「已接受」的意见,现在落地了(
|
我把这条挂了两轮,理由都是"#809/#810 在改同一个文件,怕撞车"——但一直没真去核。 这轮核了,答案很具体: 两个 rest.md 里的失效 pin 共 25 处 与 #809/#810 的改动行段重叠 = 7 处 ← 本轮不动 安全可改 = 18 处 也就是说"怕撞车"只挡得住 28%,我却拿它当了两轮不做的理由。 撞车的 7 处(按 (文件,行) 精确排除,不是按文件跳过): zh 52 / en 54 tools.ts#L521 与 #810 的 49-55 / 51-57 重叠 zh 1545 / en 1491 tools.ts#L571 与 #810 的 1539-1548 / 1485-1494 重叠 zh 1547 / en 1493 push.ts#L38 同上 en 398 auth.ts#L184 与 #810 的 395-401 重叠 改掉的 9 个唯一 pin(中英各一处,共 18): auth.ts#L7 → interface AuthUser auth.ts#L99 → invalid username or password(全仓 2 处) auth.ts#L102 → // User token (utok_) — not bound to network, … auth.ts#L243 → generateToken(全仓 3 处) auth.ts#L269 → network_id IS NULL AND token_id != auth.ts#L374 → "auto-join", "full" db.ts#L168 → CREATE TABLE IF NOT EXISTS networks push.ts#L11 → const clients = new Map<string, SSEClient[]>() push.ts#L35 → { type: "connected", session: sessionName 替换按 (文件,行号) 定向,不是全局字符串替换 —— pre-pr-selfcheck §13 刚写完, 这次照做了。其中两条(push.ts:11 和 auth.ts:243)是复合标签 (`[`push.ts:11` `clients`]`),第一遍正则没匹配上,是靠"改了 7 处而不是 9 处" 这个数字对不上发现的 —— 也是 §13 说的那条经验。 连带:基线 14 → 5(门要求删 9 条);L0 预期 27/53 → 18/35。 剩下的 5 条正是上面 4 条撞车 pin 加 changelog 的 index.ts#L253。 验证(容器内,--network none): unique_pins=18 broken_pins=5 baseline_entries=5 L6 anchors_checked=67 mismatches=0 MUTATION_RED new-out-of-range-pin rc=1 MUTATION_RED stale-baseline-entry rc=1 MUTATION_RED broadcast-anchored-to-ack-inbox rc=1 RESULT: PASS 退出码 0
提醒:这个 PR 与 #843(文档行号 pin 的下限门)有一处硬耦合我在 #843 里加了 这个 PR 恰好会让基线里的条目消失:
所以合并顺序上有一个动作项:
我在 #845 的树上模拟跑过,完整输出贴在 #843 的这条评论。 不做的话,表现是「一个只改文档的 PR 把 CI 弄红了」,而红的原因看起来跟它毫无关系 —— 这条提醒的价值就在这里。 |
上一轮我在 #843/#810/#834 上贴了一条跨 PR 耦合提醒:那两个 PR 一合,基线里 对应的条目会变 stale,门就红在「请从基线里删掉」。提醒是散文,执行的人还是 得自己去数该删哪几条 —— 把一个机械操作交给了记忆力。 这一轮把它变成一条命令: python3 scripts/check-doc-source-pins.py . --write-baseline 🔴 这个开关离「一键把门变绿」只差一个条件判断,所以它**只许缩小**: 重算若会引入基线里没有的条目(= 出现了新的失效 pin),它拒绝写并退出非零。 新失效该做的是把链接改对,不是追认进基线。 L7 两个方向都断言,不只测它能用: ① 干净树:不改写,报「基线已经是最新的」,且文件字节未动 ② 注入一个新失效 pin:必须拒绝(rc≠0),且**确认基线没被写** —— 只断言"它红了"不够,要断言"它红了而且没写" ③ 造一个「引用消失」场景(把某条 pin 的引用改钉不可变 SHA): 必须删对、条数变小、表头注释保留、门随后转绿 顺带在门红的提示里直接给出这条命令,不让人再去翻文档。 验证(容器内,--network none): [L7] ① 干净树:不改写,报「已是最新」 MUTATION_RED write-baseline-refuses-new-failure rc=1 ③ 引用消失时删对了(5 → 4),表头保留,门转绿 复原后回绿 ✓ RESULT: PASS 退出码 0 一处说明:这个改动逻辑上属于 #843 的脚本,但落在链尾(#845)。理由是 #843 → #844 → #845 是一条依赖链,改在链首要把两级重新合并重跑一遍;而三个 PR 是按序 合进 main 的,落在链尾到达 main 的时间相同。写在这里免得有人以为放错了地方。
|
补充上一条提醒:现在不用手工数该删哪几条了 —— 合并后跑 |
docs(api): 修正 trial 档的两个配额数字,并把漂掉的行号 pin 改成函数名
1. trial limits 有两个数字是错的
文档 GET /api/license 的 trial 样例写:
"limits": { "max_agents": 5, "max_networks": 1, "max_tasks_day": 100 }
真实 0.8.8(干净容器 bunx --bun @sleep2agi/commhub-server@0.8.8,全新库,type=trial):
"limits": { "max_agents": 5, "max_networks": 3, "max_tasks_day": 500 }
max_networks 1→3、max_tasks_day 100→500。源码侧确认不是环境差异 ——
server/src/db.ts 的 licenses 表 schema 默认就是:
max_agents INTEGER DEFAULT 5
max_networks INTEGER DEFAULT 3
max_tasks_day INTEGER DEFAULT 500
而 /api/license 直接读这张表(server.ts:888)。
这条会影响用户决策:照文档看会以为试用期只能建 1 个网络、每天 100 个任务。
2. 同页一处行号 pin 已经漂了
第 396 行(en 398)说 plan quota "在 auth.ts:184-189 enforced"。
实测 auth.ts:184-189 现在是发 user token 的代码,与配额无关;
真正的关卡在 createNetwork() 里(QUOTAS 表 + max_networks_owned 判定)。
根因是钉行号 —— 行号必然漂。改成钉函数名。
(RELEASE-SOP 里 R367 对 sdk-deep-dive.md 做过同样的动作:取消行号 pin。)
原文里「free plan 默认 max_networks_owned=2」核过,是对的,保留。
范围
只改 docs-site 的 zh + en 两个文件,不动产品源码。
探测全部在容器里做,生产只做了只读 curl。