merge origin/staging into the beta safety net branch

The fix brief is already on staging. BUG-1354 on this branch collided with an earlier answer-effort record, so the safety-net entries move to BUG-1355 through BUG-1357.

Co-Authored-By: Grok (agent loop)
This commit is contained in:
jesse-ux
2026-10-15 16:04:45 +08:00
co-authored by Grok (agent loop)
35 changed files with 2074 additions and 91 deletions
+94 -19
View File
@@ -15304,17 +15304,17 @@
## BUG-1140 | staging 镜像打包失败:`frontend/scripts` 下的研究脚本引用了 `frontend/tests` 的夹具
- 状态:resolved(修复已推 staging;以门禁 publish 通过与 `/api/health` 部署版本为最终证据)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-14
- 首次发现 / 最近更新:2026-10-01 / 2026-10-15
- 影响面:Gitea `backend-quality-gate` 的 publish 作业(`deploy/railway-web.Dockerfile` 构建阶段 `npm run build`)。validate 通过、publish 失败,未部署,staging 停在上一版。
- 用户现象:盘型口径(BUG-1115~1117、1131)推送后 staging 不更新。
- 触发条件:`frontend/scripts/rectification-segment-persisted-replay.ts`(盘型口径研究的 PG 持久回放 harness)`import … from "../tests/helpers/postgres-fixture.ts"`。web 镜像只复制 `frontend/src`、`frontend/scripts` 等,不复制 `frontend/tests`;`frontend/tsconfig.json` 的 include 为 `**/*.ts`,`next build` 类型检查覆盖 scripts,于是 `TS2307 Cannot find module '../tests/helpers/postgres-fixture.ts'`。
- 根因:本地与验收机都有 `frontend/tests`,`tsc --noEmit` 与 `next build` 均通过,门禁 validate 也在完整检出上跑,没有任何一步按镜像的复制清单构建。
- 修复:harness 移到 `frontend/tests/research/rectification-segment-persisted-replay.ts`(相对路径随之调整),Python driver `scripts/research/varga_resolution_persisted_replay.py` 指向新路径;新增 `frontend/tests/runtime-sources-no-test-imports.test.ts`:`src/` 与 `scripts/` 不得 import / `new URL` 引用 `tests/`。
- 验证:按 `railway-web.Dockerfile` 的 COPY 清单从提交 `git archive` 出构建目录,修复前 `npm run build` 报上述 TS2307、exit 1,修复后 exit 0、`/` Static;守卫测试 2/2,且对旧 harness 文本判定为违规;移动后 harness 1 例 ±10 冒烟 `runner_exit=0`。
- 防复发:守卫测试进 `npm test`(门禁 validate 必跑);错误台账 ERR-113。2026-10-14 这道守卫在合入前拦住了同一形状:issue #58 的往返对照脚本曾放在 `frontend/scripts/` 并导入测试夹具。脚本改放到 `frontend/tests/bench-local-pg-roundtrips.ts`,文件名不是 `.test.ts`,不会被全量测试收进去。业务代码没有改。原任务书里的旧路径保持不动。
- 防复发:守卫测试进 `npm test`(门禁 validate 必跑);错误台账 ERR-113。2026-10-14 这道守卫在合入前拦住了同一形状:issue #58 的往返对照脚本曾放在 `frontend/scripts/` 并导入测试夹具。脚本改放到 `frontend/tests/bench-local-pg-roundtrips.ts`,文件名不是 `.test.ts`,不会被全量测试收进去。业务代码没有改。原任务书里的旧路径保持不动。2026-10-15 issue #65 的请求体探针也是同一形状:`answer-effort-probe.mts` 放在 `frontend/scripts/research/`,用 `new URL` 去读 `frontend/tests` 的夹具。探针挪到 `frontend/tests/answer-effort-probe.mts`,文件名不是 `.test.ts`。业务代码没有改。
- 相关记录:BUG-1115~1117、BUG-1131、BUG-1327。
- 复发自:无。2026-10-14 是同一形状,被守卫拦住,未进测试环境。
- 修复版本:`codex/staging-publish-fix-20261001`。2026-10-14 的同类挪动在 `codex/node-runtime-resources-20261013`。
- 复发自:无。2026-10-14 与 2026-10-15 都是同一形状,被守卫拦住,未进测试环境。
- 修复版本:`codex/staging-publish-fix-20261001`。2026-10-14 的同类挪动在 `codex/node-runtime-resources-20261013`。2026-10-15 的同类挪动在 `codex/answer-effort-ab-20261013`。
## BUG-1141 | 生时校正缺少度数级时间信号(角度类技法研究)
@@ -17991,6 +17991,36 @@
- 复发自:无。
- 修复版本:分支 `codex/api-disk-cache-retention-20261013`,尚未合入 staging。
## BUG-1333 | 合盘排盘漏关外部概览,而且两张盘排队算
- 状态:fixed-pending-verify
- 首次发现 / 最近更新:2026-10-13 / 2026-10-15
- 来源:issue #64。任务书 `docs/tasks/TASK-api-chart-subprocess-gate-20261013.md` T1、T3、T4。
- 现象:合盘每次要排两张盘,都没关掉用不上的外部概览。每张盘多起两个后台进程,而且两张盘一张一张排。名额占满时,日志里看不出是谁占着。
- 触发条件:打开合盘。排盘接口本身默认仍走完整链,给报告和外部调用留着。
- 根因:合盘这条路由没跟上「只读盘面就关掉外部概览」的约定,也没有合同测试拦住。名额按路径一刀切,不带 skip 的排盘默认不占名额。
- 修复:合盘两次排盘都带上关闭外部概览,两张盘同时排;婚恋分支的两张分盘也同时排。前端里凡是发给排盘接口的请求体,都要带这个开关,由源码合同守着。另加一个默认关闭的开关:打开后,不带 skip 的同步排盘才占一个重计算名额。名额占满时记一行日志,只写被拒的路径和当前占用者的路径、已占秒数,不写请求正文。
- 验证:`frontend/tests/synastry-chart-skip.test.ts`、`frontend/tests/chart-request-skip-contract.test.ts`、`tests/test_api_chart_subprocess_gate.py`、`tests/test_api_heavy_compute_gate.py`。合盘耗时见 `docs/testing/api-chart-subprocess-gate-ab-20261013.md`。`tests/test_supabase_user_data_contract.py` 锁住合盘路由向 `/api/chart` 请求本人和对方两张盘,且两次都带 `skip_vedastro_main_entry_overview: true`。
- 防复发:源码合同要求 `frontend/src/app/api` 和 `frontend/src/lib` 里发往 `/api/chart` 的请求体带 `skip_vedastro_main_entry_overview: true`。Python 合同按路由原文锁住这两次调用和这个开关。`/api/chart` 仍不在重计算路径名单里。
- 相关记录:BUG-161、BUG-715、BUG-717、BUG-1302。
- 复发自:无。
- 修复版本:分支 `codex/api-chart-subprocess-gate-20261013`。编号是任务书预留的空号,夹在 BUG-1332 和 BUG-1335 之间。
## BUG-1334 | 个人报告在没有参照引擎时仍为每次报告启动子进程
- 状态:fixed-pending-verify
- 首次发现 / 最近更新:2026-10-13 / 2026-10-15
- 来源:issue #64。任务书 T2。
- 现象:每份个人报告会起大约 10 个后台进程。其中参照引擎没装时,年度回放注定失败,但仍要付启动开销。另一段静态校验和用户的盘无关,每份报告都重跑。
- 触发条件:生成个人长报告,且这台机器没有安装 PyJHora。装了参照引擎的开发机上,隔离用的子进程仍然要留着。
- 根因:子进程是为了把参照引擎隔离开,这是对的;父进程却没有先问「这台机器根本没装」。静态校验文件被挂在每份报告上,没有按文件变化复用。
- 修复:父进程先看一次能不能导入参照引擎,不能就不起子进程,返回和「子进程在没装时」相同的结果。解释器查找和静态校验各在进程内记一次;校验文件变了会重算。不缓存任何出生资料。装了参照引擎时仍走子进程。
- 验证:`tests/test_api_chart_subprocess_gate.py` 对照 `tests/fixtures/api-chart-subprocess-gate/`。生产上的收益大约是每份报告 0.5 到 0.9 秒、少掉那几个注定失败的进程,不是排查时在装了参照引擎的机器上看到的 5 秒。本机没有 PyJHora,实测见 `docs/testing/api-chart-subprocess-gate-ab-20261013.md`。
- 防复发:无参照引擎时测试断言不会调用 `subprocess.run`,返回值与固定样本一致。
- 相关记录:BUG-721、BUG-1265、BUG-1333。
- 复发自:无。
- 修复版本:分支 `codex/api-chart-subprocess-gate-20261013`。
## BUG-1335 | 个人长报告的中文出稿和溯源哈希重复劳动
- 状态:investigating
@@ -33372,17 +33402,17 @@
## BUG-1140 | staging 镜像打包失败:`frontend/scripts` 下的研究脚本引用了 `frontend/tests` 的夹具
- 状态:resolved(修复已推 staging;以门禁 publish 通过与 `/api/health` 部署版本为最终证据)
- 首次发现 / 最近更新:2026-10-01 / 2026-10-14
- 首次发现 / 最近更新:2026-10-01 / 2026-10-15
- 影响面:Gitea `backend-quality-gate` 的 publish 作业(`deploy/railway-web.Dockerfile` 构建阶段 `npm run build`)。validate 通过、publish 失败,未部署,staging 停在上一版。
- 用户现象:盘型口径(BUG-1115~1117、1131)推送后 staging 不更新。
- 触发条件:`frontend/scripts/rectification-segment-persisted-replay.ts`(盘型口径研究的 PG 持久回放 harness)`import … from "../tests/helpers/postgres-fixture.ts"`。web 镜像只复制 `frontend/src`、`frontend/scripts` 等,不复制 `frontend/tests`;`frontend/tsconfig.json` 的 include 为 `**/*.ts`,`next build` 类型检查覆盖 scripts,于是 `TS2307 Cannot find module '../tests/helpers/postgres-fixture.ts'`。
- 根因:本地与验收机都有 `frontend/tests`,`tsc --noEmit` 与 `next build` 均通过,门禁 validate 也在完整检出上跑,没有任何一步按镜像的复制清单构建。
- 修复:harness 移到 `frontend/tests/research/rectification-segment-persisted-replay.ts`(相对路径随之调整),Python driver `scripts/research/varga_resolution_persisted_replay.py` 指向新路径;新增 `frontend/tests/runtime-sources-no-test-imports.test.ts`:`src/` 与 `scripts/` 不得 import / `new URL` 引用 `tests/`。
- 验证:按 `railway-web.Dockerfile` 的 COPY 清单从提交 `git archive` 出构建目录,修复前 `npm run build` 报上述 TS2307、exit 1,修复后 exit 0、`/` Static;守卫测试 2/2,且对旧 harness 文本判定为违规;移动后 harness 1 例 ±10 冒烟 `runner_exit=0`。
- 防复发:守卫测试进 `npm test`(门禁 validate 必跑);错误台账 ERR-113。2026-10-14 这道守卫在合入前拦住了同一形状:issue #58 的往返对照脚本曾放在 `frontend/scripts/` 并导入测试夹具。脚本改放到 `frontend/tests/bench-local-pg-roundtrips.ts`,文件名不是 `.test.ts`,不会被全量测试收进去。业务代码没有改。原任务书里的旧路径保持不动。
- 防复发:守卫测试进 `npm test`(门禁 validate 必跑);错误台账 ERR-113。2026-10-14 这道守卫在合入前拦住了同一形状:issue #58 的往返对照脚本曾放在 `frontend/scripts/` 并导入测试夹具。脚本改放到 `frontend/tests/bench-local-pg-roundtrips.ts`,文件名不是 `.test.ts`,不会被全量测试收进去。业务代码没有改。原任务书里的旧路径保持不动。2026-10-15 issue #65 的请求体探针也是同一形状:`answer-effort-probe.mts` 放在 `frontend/scripts/research/`,用 `new URL` 去读 `frontend/tests` 的夹具。探针挪到 `frontend/tests/answer-effort-probe.mts`,文件名不是 `.test.ts`。业务代码没有改。
- 相关记录:BUG-1115~1117、BUG-1131、BUG-1327。
- 复发自:无。2026-10-14 是同一形状,被守卫拦住,未进测试环境。
- 修复版本:`codex/staging-publish-fix-20261001`。2026-10-14 的同类挪动在 `codex/node-runtime-resources-20261013`。
- 复发自:无。2026-10-14 与 2026-10-15 都是同一形状,被守卫拦住,未进测试环境。
- 修复版本:`codex/staging-publish-fix-20261001`。2026-10-14 的同类挪动在 `codex/node-runtime-resources-20261013`。2026-10-15 的同类挪动在 `codex/answer-effort-ab-20261013`。
## BUG-1141 | 生时校正缺少度数级时间信号(角度类技法研究)
@@ -36044,6 +36074,36 @@
- 复发自:无。
- 修复版本:分支 `codex/api-disk-cache-retention-20261013`,尚未合入 staging。
## BUG-1333 | 合盘排盘漏关外部概览,而且两张盘排队算
- 状态:fixed-pending-verify
- 首次发现 / 最近更新:2026-10-13 / 2026-10-15
- 来源:issue #64。任务书 `docs/tasks/TASK-api-chart-subprocess-gate-20261013.md` T1、T3、T4。
- 现象:合盘每次要排两张盘,都没关掉用不上的外部概览。每张盘多起两个后台进程,而且两张盘一张一张排。名额占满时,日志里看不出是谁占着。
- 触发条件:打开合盘。排盘接口本身默认仍走完整链,给报告和外部调用留着。
- 根因:合盘这条路由没跟上「只读盘面就关掉外部概览」的约定,也没有合同测试拦住。名额按路径一刀切,不带 skip 的排盘默认不占名额。
- 修复:合盘两次排盘都带上关闭外部概览,两张盘同时排;婚恋分支的两张分盘也同时排。前端里凡是发给排盘接口的请求体,都要带这个开关,由源码合同守着。另加一个默认关闭的开关:打开后,不带 skip 的同步排盘才占一个重计算名额。名额占满时记一行日志,只写被拒的路径和当前占用者的路径、已占秒数,不写请求正文。
- 验证:`frontend/tests/synastry-chart-skip.test.ts`、`frontend/tests/chart-request-skip-contract.test.ts`、`tests/test_api_chart_subprocess_gate.py`、`tests/test_api_heavy_compute_gate.py`。合盘耗时见 `docs/testing/api-chart-subprocess-gate-ab-20261013.md`。`tests/test_supabase_user_data_contract.py` 锁住合盘路由向 `/api/chart` 请求本人和对方两张盘,且两次都带 `skip_vedastro_main_entry_overview: true`。
- 防复发:源码合同要求 `frontend/src/app/api` 和 `frontend/src/lib` 里发往 `/api/chart` 的请求体带 `skip_vedastro_main_entry_overview: true`。Python 合同按路由原文锁住这两次调用和这个开关。`/api/chart` 仍不在重计算路径名单里。
- 相关记录:BUG-161、BUG-715、BUG-717、BUG-1302。
- 复发自:无。
- 修复版本:分支 `codex/api-chart-subprocess-gate-20261013`。编号是任务书预留的空号,夹在 BUG-1332 和 BUG-1335 之间。
## BUG-1334 | 个人报告在没有参照引擎时仍为每次报告启动子进程
- 状态:fixed-pending-verify
- 首次发现 / 最近更新:2026-10-13 / 2026-10-15
- 来源:issue #64。任务书 T2。
- 现象:每份个人报告会起大约 10 个后台进程。其中参照引擎没装时,年度回放注定失败,但仍要付启动开销。另一段静态校验和用户的盘无关,每份报告都重跑。
- 触发条件:生成个人长报告,且这台机器没有安装 PyJHora。装了参照引擎的开发机上,隔离用的子进程仍然要留着。
- 根因:子进程是为了把参照引擎隔离开,这是对的;父进程却没有先问「这台机器根本没装」。静态校验文件被挂在每份报告上,没有按文件变化复用。
- 修复:父进程先看一次能不能导入参照引擎,不能就不起子进程,返回和「子进程在没装时」相同的结果。解释器查找和静态校验各在进程内记一次;校验文件变了会重算。不缓存任何出生资料。装了参照引擎时仍走子进程。
- 验证:`tests/test_api_chart_subprocess_gate.py` 对照 `tests/fixtures/api-chart-subprocess-gate/`。生产上的收益大约是每份报告 0.5 到 0.9 秒、少掉那几个注定失败的进程,不是排查时在装了参照引擎的机器上看到的 5 秒。本机没有 PyJHora,实测见 `docs/testing/api-chart-subprocess-gate-ab-20261013.md`。
- 防复发:无参照引擎时测试断言不会调用 `subprocess.run`,返回值与固定样本一致。
- 相关记录:BUG-721、BUG-1265、BUG-1333。
- 复发自:无。
- 修复版本:分支 `codex/api-chart-subprocess-gate-20261013`。
## BUG-1335 | 个人长报告的中文出稿和溯源哈希重复劳动
- 状态:investigating
@@ -36182,7 +36242,22 @@
- 复发自:无
- 修复版本:分支 `codex/chart-ascendant-row-20261015`。
## BUG-1354 | 生产没有每日自动备份
## BUG-1354 | 低强度推理设置写在嵌套字段里,供应商收不到
- 状态:investigating
- 首次发现 / 最近更新:2026-10-13 / 2026-10-15
- 来源:issue #65。任务书 `docs/tasks/TASK-answer-effort-ab-20261013.md`。请求体拦截见 `docs/testing/answer-effort-ab-20261013.md`。
- 现象:代码里有一处把推理强度写成 `low`,但真正发出去的请求没有 `reasoning_effort` 这个字段。强度被包在 `thinking` 里面。
- 触发条件:调用 `consultationSliceGenerationSettings`。产品代码里没有调用方,线上对话不受影响。
- 根因:兼容通道只认供应商选项最外层的 `reasoningEffort`,并把它写成 `reasoning_effort`。嵌在 `thinking` 里的同名键会原样送出去,供应商不把它当强度。
- 修复:本单不改这处形状,也不改它的两条断言。答题步的新开关写在最外层。要不要删掉或改掉这处旧形状,等产品拍板。
- 验证:`frontend/tests/answer-effort-probe.mts --dry-run`。这处设置的请求体是 `thinking.reasoningEffort = "low"`,`reasoning_effort` 为空。新开关设成 `low` 时,答题步请求体有 `reasoning_effort: "low"`。
- 防复发:新测试锁住「开关不设时请求体与改前一致」,并锁住新强度出现在最外层。
- 相关记录:BUG-148、BUG-1142。
- 复发自:无。
- 修复版本:未修。分支 `codex/answer-effort-ab-20261013` 只记录。原先写在 BUG-1351,该号已被数据层占用;按本文件规则保留较早的 BUG-1351,本条改为 BUG-1354,接在 BUG-1353 之后。
## BUG-1355 | 生产没有每日自动备份
- 状态:代码已交,主机安装待产品
- 首次发现 / 最近更新:2026-10-14 / 2026-10-15
@@ -36193,37 +36268,37 @@
- 修复:新增只从 `main` 运行的每日备份工作流,复用现有加密与还原校验脚本。每日文件用单独前缀,过期清理不会碰到手动恢复点。工作流要等进入 `main` 后才会在北京时间 04:00 跑。本机没有生产主机,没有手动触发。
- 验证:合同测试锁定有每日时刻、只在 `main` 上跑、调用同一个恢复脚本、清理只删过期的每日前缀。虚构目录里,手动恢复点和其他文件都还在。
- 防复发:上述合同测试。清理函数不接受外部传入的前缀。
- 相关记录:BUG-1350、BUG-1355、BUG-1356。
- 相关记录:BUG-1350、BUG-1356、BUG-1357。
- 复发自:无。
- 修复版本:分支 `codex/beta-safety-net-20261014`。开工时本文件最大号是 BUG-1353。
- 修复版本:分支 `codex/beta-safety-net-20261014`。开工时本文件最大号是 BUG-1353,当时写成 BUG-1354。合并 staging 后该号已被更早的答题强度记录占用,按台账规则改为 BUG-1355。
## BUG-1355 | 站点打不开或磁盘过满时没有告警
## BUG-1356 | 站点打不开或磁盘过满时没有告警
- 状态:代码已交,主机安装待产品
- 首次发现 / 最近更新:2026-10-14 / 2026-10-15
- 来源:同 BUG-1354。
- 来源:同 BUG-1355。
- 现象:容器自己的健康检查只会尝试重启,不会通知人。磁盘变满也没有通知。
- 触发条件:公网健康检查连续失败,或生产主机磁盘使用率达到 80%。
- 根因:上线时没有持续探活,也没有把告警发到人用的渠道。
- 修复:探活脚本连续 3 次失败才发一条告警,恢复只发一次。磁盘检查单独在生产主机上跑。Webhook 从环境文件读取,没有配置就只写本机日志。外发正文不含用户内容。脚本还没装到 runner 或生产主机上。
- 验证:本地桩服务覆盖正常、500、超时和缺少字段。连续 3 次才告警,恢复只出现一次。签名测试里的密钥没有出现在发出的正文里。
- 防复发:上述脚本测试。告警行若含不允许的字段,脚本拒绝发送。
- 相关记录:BUG-1354、BUG-1356。
- 相关记录:BUG-1355、BUG-1357。
- 复发自:无。
- 修复版本:分支 `codex/beta-safety-net-20261014`。编号接在 BUG-1354 之后。
- 修复版本:分支 `codex/beta-safety-net-20261014`。原先写成 BUG-1355,合并 staging 后顺延为 BUG-1356。
## BUG-1356 | 容器日志不轮转,也不能按一轮请求查找
## BUG-1357 | 容器日志不轮转,也不能按一轮请求查找
- 状态:代码已交,主机安装待产品
- 首次发现 / 最近更新:2026-10-14 / 2026-10-15
- 来源:同 BUG-1354。BUG-1306 取证时日志只在容器控制台,重建后查不到。
- 来源:同 BUG-1355。BUG-1306 取证时日志只在容器控制台,重建后查不到。
- 现象:编排没有日志大小上限。知道一轮请求的编号时,没有只读的查找命令。
- 触发条件:日志持续写入,或需要按 requestId 回看 web、api、caddy。
- 根因:日志沿用守护进程的未知默认值,也没有按编号检索的入口。
- 修复:每个服务的日志改为单文件 20 MB、最多 10 个文件。新增只读查找脚本,只接受 UUID。错误汇总只计数和最多 5 个编号,丢掉含邮箱、额外日期或非 ASCII 文本的行。要等生产容器按新编排重建后,轮转才生效。
- 验证:两个编排文件里每个服务的日志段一致。虚构日志能按时间排序找到编号;命令注入写法被拒绝,旁边的标记文件还在。含邮箱和汉字的样本没有进入告警文本。
- 防复发:上述合同测试和查找脚本的格式检查。
- 相关记录:BUG-1306、BUG-1354、BUG-1355。
- 相关记录:BUG-1306、BUG-1355、BUG-1356。
- 复发自:无。
- 修复版本:分支 `codex/beta-safety-net-20261014`。编号接在 BUG-1355 之后。
- 修复版本:分支 `codex/beta-safety-net-20261014`。原先写成 BUG-1356,合并 staging 后顺延为 BUG-1357。
@@ -0,0 +1,43 @@
# PROGRESS · 答题步推理强度开关(2026-10-15)
- 分支:`codex/answer-effort-ab-20261013`
- 实际基线:`origin/staging` `9274491681f6fcacbc87e3797441d2c5221de874`
- 前置 `consult-prep-parallel` 已在 `origin/staging` 上(`git merge-base --is-ancestor` 退出码 0)
- 默认行为不变。没有模型钥匙,A/B 未跑。
## 做了什么
1. 新环境变量 `CONSULT_ANSWER_REASONING_EFFORT`。`low` / `medium` / `high` / `max` 才生效。空着或没设,请求体与改前一致。非法值 `console.warn` 一次,然后当作没设。
2. `agentGenerationSettings` 增加 `providerReasoningEffort`。它写在供应商选项的最外层,兼容通道据此发出 `reasoning_effort`。原来的 `reasoningEffort` 仍嵌在 `thinking` 里,切片那两条断言不用改。
3. 答题步才带这个字段。判据与答案时钟相同:计算结果已经在手(`answerReady`),或者公开日历从第一步就是答题(`answerFromStart`)。主循环第 0 步计算结果还没到,不带。重试是新的一轮,步号从 0 重新计,但计算结果已经在手,所以重试的第 0 步也带。续写仍是关闭思考,不带这个字段。
4. Mastra 的 `prepareStep` 会按步合并 `providerOptions`(`mergeObjects`)。答题步返回的强度会并进这一步,不会整轮都带。本单按步生效,没有改成整轮。
5. 生平回测脚本增加 `--cases figure:domain,…` 和 `--efforts unset,low,medium`。每行记下端到端秒数、第 0 / 1 步秒数和推理 token、输出 token、结束原因、是否空答、是否截断、正文字数。摘要记下当次强度。
## 自测
| 检查 | 命令 | 结果 |
| --- | --- | --- |
| 请求体拦截 | `node node_modules/tsx/dist/cli.mjs tests/answer-effort-probe.mts --dry-run` | 退出码 0。数字见 `docs/testing/answer-effort-ab-20261013.md`。2026-10-15 打回后脚本从 `frontend/scripts/research/` 挪到 `frontend/tests/`,命令改成这一条。 |
| 本单测试与任务书点名的四份 | `node node_modules/tsx/dist/cli.mjs --test` 后接 `answer-effort-switch-20261013`、`consultation-agentic-runtime`、`consultation-workflow-contract`、`consult-answer-clock-20261001`、`consult-latency-timing-20261005` | 114 pass,0 fail,0 cancelled |
| 类型检查 | 在 `frontend` 下 `.\node_modules\.bin\tsc --noEmit -p .` | 退出码 0 |
| lint | 在 `frontend` 下 `npm run lint` | 退出码 0,0 error,123 条原有 warning。探针里多出来的 1 条未使用变量已去掉,改过的文件再跑 eslint 是 0 |
开关不设时,两步请求体与改前保存的整份 JSON 逐字节一致。设 `low` 时只有答题步出现 `reasoning_effort: "low"`,第 0 步没有;续写仍是 `thinking: disabled` 且没有 `reasoning_effort`。
## 改既有断言
| 原值 | 新值 | 原因 |
| --- | --- | --- |
| `await streamWithOverflowRetry(agent)` | `await streamWithOverflowRetry(agent, generalStreamOptions)` | 公开日历从第一步就是答题。不传选项就没有 `prepareStep`,强度加不上去。开关不设时这些选项不改请求体。答案时钟的断言还在。 |
切片的嵌套形状和它的两条断言没有改。
## 需拍板
- 默认强度。本单不改。有钥匙的验收按任务书 T3 出数之后,再决定设不设、设哪一档。部署默认写在哪里也留到那次。
- `consultationSliceGenerationSettings` 删掉还是改成最外层。它没有调用方。本单不改。见 BUG-1354(原先写成 BUG-1351,该号已被数据层占用)。
- 若供应商不接受降档,对照组 D(答题步关思考)只供参考,采用与否另说。本机没跑。
## 环境缺口
A/B 未跑:缺模型 key。没有 `DS_KEY`,也没有 `BACKTEST_MODEL_KEY`。
@@ -0,0 +1,40 @@
# PROGRESS · 答题推理强度开关修复(2026-10-15)
- 分支:`codex/answer-effort-ab-20261013`
- 被打回的提交:`ab7f66be14d93a5ccbc3433f3c44e134a65b1e21`
- 修复单:`docs/tasks/TASK-answer-effort-ab-fix-20261015.md`
- 已合并 `origin/staging` `71f7fac3a13f817f91755ff01b25b8706380ae19`,合并提交 `0d73cd332bf4efae2b8ec9389d5853384f4616f4`。没有改写已推送的历史。
## 做了什么
1. 把 `frontend/scripts/research/answer-effort-probe.mts` 挪到 `frontend/tests/answer-effort-probe.mts`。文件名不是 `.test.ts`,也不是 `.test.tsx`,全量测试不会把它收成一条用例。
2. 相对路径改过:源码从 `../src/...` 引入,夹具改成 `./fixtures/answer-effort-switch-20261013.json`。本单测试改为从同目录引入。已经保存的夹具文件没有重写。
3. 命令同步进 `docs/tasks/PROGRESS-answer-effort-ab-20261013.md` 和 `docs/testing/answer-effort-ab-20261013.md`。
4. 业务代码没改。开关默认值没改。任务书里留到拍板的默认强度,以及没人调用的旧嵌套设置,都没做。
5. 合并时,本分支原来的调查记录占用了 BUG-1351,和数据层的 BUG-1351 撞号。保留 staging 上的 BUG-1351、BUG-1352、BUG-1353。这条调查记录改为 BUG-1354。BUG-1140 补记了这次同一形状的挪动。
## 自测
| 检查 | 命令 | 结果 |
| --- | --- | --- |
| 镜像守卫和本单测试 | 在 `frontend` 下 `node node_modules/tsx/dist/cli.mjs --test tests/runtime-sources-no-test-imports.test.ts tests/answer-effort-switch-20261013.test.ts` | 5 pass,0 fail,0 cancelled |
| 请求体拦截 | 在 `frontend` 下 `node node_modules/tsx/dist/cli.mjs tests/answer-effort-probe.mts --dry-run` | 退出码 0 |
| 类型检查 | 在 `frontend` 下 `.\node_modules\.bin\tsc --noEmit -p .` | 退出码 0 |
| lint | 在 `frontend` 下 `npm run lint` | 退出码 0,0 error,123 warning |
| 隐私标记与 bug 记录格式 | 在工作树根 `.venv\Scripts\python.exe -m pytest tests/test_repo_privacy_markers.py tests/test_bug_history_workflow.py -q` | 64 pass,0 fail,退出码 0 |
守卫的两条都在上面 5 条里面:镜像目录不再引用测试目录,并且仍认得当初弄坏构建的那几种写法。
## prepareStep 会不会盖掉原有选项
不会整份换掉。开关打开时,答题步返回的供应商选项来自 `agentGenerationSettings`,Mastra 按步做深合并,并进这一步已有的选项。思考仍是打开。输出长度上限在基础流的模型设置里,这一步没有返回它,所以上限还在。缓存标记写在消息上,不在这份流选项里。因此没有往 `BLOCKED.md` 新写一条「被覆盖」。
## 偏离
- 没有跑前端全量 `npm test`。这一侧的约定是不跑全量,留给验收。本机只跑了打回指出的那条守卫,以及本单测试。
- 修复单写的 `git diff ab7f66be..<新提交> -- frontend/src` 不是空的。原因是按修复单先合并了最新 staging,staging 自己改过星盘页和数据层。本轮修复相对合并提交 `0d73cd33`,`frontend/src` 没有差异。业务代码和开关默认值没动。
- A/B 未跑:缺模型 key。没有把请求体拦截写成对照已通过。
## 环境缺口
A/B 未跑:缺模型 key。`BLOCKED.md` 里原有的这一条保留,没有新增。
@@ -0,0 +1,90 @@
# PROGRESS · 合盘排盘与报告多余子进程(2026-10-15)
- 分支:`codex/api-chart-subprocess-gate-20261013`
- 实际基线:`origin/staging` `9274491681f6fcacbc87e3797441d2c5221de874`(任务书写的 `77057a819` 更早)
- `scripts/jyotish_api_server.py`:开工 10944 行,做完 10940 行
- BUG-1333、BUG-1334。`docs/BUG_HISTORY.md` 后半是同一份历史的副本,两处都补上了。
## 做了什么
1. 合盘两次排盘都带 `skip_vedastro_main_entry_overview: true`,不改 `synastryBirthPayload`。两张盘和婚恋分支的两张九分盘都改成同时发出。任一失败仍走原来的 `catch`。
2. 前端 `app/api` 和 `lib` 里发往 `/api/chart` 的请求体必须带这个开关。白名单是空的。
3. 父进程先看一次能不能导入 PyJHora。不能就不起年度回放和 Tajika 的子进程,返回和「子进程在没装时」相同的结果。样本在 `tests/fixtures/api-chart-subprocess-gate/`,来自本机真实子进程(当时 `find_spec("jhora")` 为 `None`)。装了之后仍走子进程。
4. 解释器查找(含「没找到」)和静态 oracle 校验在进程内各记一次。校验按文件路径、mtime、大小作键。不缓存出生资料。
5. 开关 `JYOTISH_GATE_CHART_OVERVIEW` 默认关。只有把它设为 `1` 时,不带 skip、也不是异步排队的 `/api/chart` 才占一个重计算名额。`/api/chart` 没有加进 `HEAVY_COMPUTE_PATHS`。预热咨询不占名额的判断挪进了闸门模块。
6. 名额占满时打一行日志:被拒路径、当前占用者路径、已占秒数。不写请求正文。
## 自测
| 检查 | 命令 | 结果 |
| --- | --- | --- |
| 闸门、增长护栏、本单新测试 | `.venv\Scripts\python.exe -m pytest tests/test_api_chart_subprocess_gate.py tests/test_api_heavy_compute_gate.py tests/test_api_server_growth_contract.py -q` | 21 passed,退出码 0 |
| 报告三份既有测试 | 同上解释器 `-m pytest tests/test_report_reader_main.py tests/test_report_longform_parity.py tests/test_report_density_facts.py -q` | 27 passed,退出码 0 |
| 前端本单与相关单文件 | 在 `frontend` 下 `node node_modules/tsx/dist/cli.mjs --test tests/synastry-chart-skip.test.ts tests/chart-request-skip-contract.test.ts tests/chart-view-route.test.ts tests/ephemeris-route.test.ts` | 22 pass,0 fail |
| 类型检查 | `frontend\node_modules\.bin\tsc --noEmit -p frontend` | 退出码 0 |
| lint | 在 `frontend` 下 `npm run lint` | 退出码 0,0 error,123 条原有 warning |
| A/B | `.venv\Scripts\python.exe scripts\research\api_chart_subprocess_ab.py` | 退出码 0。数字见下 |
`grep` 这三个名字在 `tests/` 里只命中本单新测试:`annual_pyjhora_replay`、`_oracle_progress_snapshot`、`_resolve_external_pyjhora_python`。
## A/B 数字
合盘:串行不带 skip 中位 8.0596 秒、2 次 `subprocess.run`;并发带 skip 中位 0.0166 秒、0 次。盘面摘要逐字节一致。耗时约为对照的 0.2%。
报告:强制仍起子进程中位 16.5322 秒、7 次;短路后 15.3235 秒、1 次。去掉易变字段后逐字节一致。少掉的 6 次是年度回放和 Tajika。
名额模拟(种子 64,一分钟,未改规则):
| 方案 | 咨询被拒 | 报告被拒 | 校正被拒 | 最高并发 | 碰到不得加池的那条 |
| --- | --- | --- | --- | --- | --- |
| O1 现状 | 40% | 0% | 17% | 2 | 否 |
| O2 咨询单独小池 | 0% | 0% | 17% | 3 | 是 |
| O3 咨询最多再等 1.5 秒 | 30% | 0% | 33% | 2 | 否 |
| O4 重任务最多占 1 个 | 0% | 0% | 67% | 2 | 否 |
全文在 `docs/testing/api-chart-subprocess-gate-ab-20261013.md`。
## 改既有断言
无。原值 / 新值 / 原因:没有改旧断言。
## 偏离
- `/api/health` 没有加 `heavy_compute` 字段。状态函数 `heavy_compute_status()` 在闸门模块里,接到健康检查会让主文件变长。任务书允许砍掉这一项。
- 星盘名额开关默认关。打开会让不带 skip 的外部调用可能拿到 429,任务书要求先有 A/B 再提议,本单不打开。
- O2、O3、O4 没有实现。
- 报告对照不是 staging 源码另导入,而是本分支把「已安装」强制打开。静态校验缓存两边都在,所以耗时差只反映那 6 个子进程。
- 合盘子进程是 2 → 0,不是任务书举例的 4 → 0。这台机器两张盘合计只数到 2 次 `subprocess.run`。
- 没有「装了 PyJHora」的计时。父进程强制当成已安装时,子进程仍会因为没装而失败,这一组用来代表「不短路就要付启动开销」。
## 环境缺口
- 本机 `.venv`:`sys.executable` 是工作树的 `python.exe`;`shutil.which("python3")` 是 Windows 应用商店占位;`python3.14` 没有;`find_spec("jhora")` 是 `None`。
- Docker 在,没有本地 `jyotisha-api` 镜像,镜像里的解释器路径没有核实。
- 生产装了 VedAstro、并且开了外网时,不带 skip 的排盘真实耗时本机测不到。
- 名额被占满后连带拒绝的真实频率,要等日志上了 staging 再看。
## 需拍板
轻重任务要不要分开限流,本单不改。模拟里,让咨询少被拒,校正就会多被拒;给咨询单独开池会让同时在跑的个数超过 2。要改规则需要另立一单。
## 默认值
- `JYOTISH_GATE_CHART_OVERVIEW` 默认关。
- 合盘 skip 和报告短路没有开关。
## 2026-10-15 打回修复
合盘路由的合同跟上了新写法:仍锁本人和对方各请求一次排盘,并锁住关闭外部概览。对照脚本默认写到系统临时目录,只有带 `--out` 才写指定文件,不再覆盖已入库的记录。业务代码没改。名额配额仍不实现。没有新 bug 编号,补在 BUG-1333 的两处副本上。
| 检查 | 命令 | 结果 |
| --- | --- | --- |
| 用户数据合同 | `.venv\Scripts\python.exe -m pytest tests/test_supabase_user_data_contract.py -q` | 8 passed,退出码 0 |
| 脚本默认路径 | 调用 `resolve_record_path` | 默认不在仓库内,也不是已入库的 json;`--out` 指向传入路径 |
| 源码文本检索 | `tests` 与 `frontend/tests` 里的合盘路由、`postPython("/api/chart"`、`postPython("/api/varga_full"` | 只有这一条合同在断言旧写法,已改。其它命中是在读路由文件,没有断言旧的两次调用。分盘前缀仍匹配,没改 |
| 类型检查 | 在 `frontend` 下 `node_modules\.bin\tsc --noEmit -p .` | 退出码 0 |
| lint | 在 `frontend` 下 `npm run lint` | 退出码 0,0 error,123 条原有 warning |
原值:`postPython("/api/chart", selfPayload)` 与 `postPython("/api/chart", partnerPayload)`。新值:两次调用都展开对应 payload,并带 `skip_vedastro_main_entry_overview: true`。原因:合盘改为同时排两张盘并关掉用不上的外部概览,合同要锁住这两次请求和这个开关,不放宽。
偏离:先合并了 `origin/staging` `1de3cc307be9134a3b8d1d606c738c1468414118`。相对打回 SHA `336286e4`,`frontend/src` 里多出来的是数据访问层那一单,不是这次修复改的。技能目录下的同名脚本没改,它的输出根不是仓库的 `docs/testing`。
@@ -3,7 +3,7 @@
- 分支:`codex/beta-safety-net-20261014`
- 基线:开工时 `origin/staging` 为 `71f7fac3a13f817f91755ff01b25b8706380ae19`
- 前置:`09906cdc`(发布通路)和 `4c3485bc`、`3893783e`(#58)都已是这个基线的祖先。`deploy/docker-compose.server.yml` 在加日志段之前没有 #58 留下的未合入冲突。
- 编号:本文件最大号是 BUG-1353。本单用 BUG-1354(没有每日备份)、BUG-1355(没有探活和磁盘告警)、BUG-1356(日志不轮转、不能按请求编号查)。
- 编号:本文件最大号是 BUG-1353。本单当时写成 BUG-1354(没有每日备份)、BUG-1355(没有探活和磁盘告警)、BUG-1356(日志不轮转、不能按请求编号查)。合并 staging 后 BUG-1354 已被更早的答题强度记录占用,三条顺延为 BUG-1355、BUG-1356、BUG-1357。
## 做了什么
+7 -5
View File
@@ -338,8 +338,10 @@
| `TASK-release-path-production-20261014.md` | `PROGRESS-release-path-production-20261014.md` | **发布通路 + 生产首发**:产品 10-14 授权改生产工作流(发布 SHA = 最近门禁成功提交 + 其后纯文档);63 个迁移清单与演练脚本、首发 runbook;生产拒绝测试验证码。Grok 不 dispatch 生产工作流 | 第 2 次打回后续修 | `codex/release-path-production-20261014`(BUG-1348、BUG-1349) |
| `TASK-release-path-production-fix-20261014.md` | `PROGRESS-release-path-production-fix-20261014.md` | **#74 第 1 次打回**:发布规则成立;测试写死 Windows 路径(Linux 全红)、主机演练依赖主机 node 且会留下生产数据副本、docs-only 头时回滚被拒;另补 CHECK_RELEASE_GATE 断言与 OTP 守卫规范化 | 已修,第 2 次仍未过 | `codex/release-path-production-20261014` |
| `TASK-release-path-production-fix2-20261014.md` | `PROGRESS-release-path-production-fix2-20261014.md` | **#74 修复单 2(产品选 A)**:恢复点要带属主与权限,旧格式不得回退;演练按备份里的授权比对,并在真实 PostgreSQL 17 上跑通一次 | 待验收 | `codex/release-path-production-20261014` |
| `TASK-beta-safety-net-20261014.md` | `PROGRESS-beta-safety-net-20261014.md` | **内测安全网**:每日备份 + 恢复演练、探活告警、本地错误汇总、日志轮转、按 requestId 查日志。发布通路与 #58 已在开工时的 `origin/staging` 上。主机安装和一次演练留给产品 | 待验收 | `codex/beta-safety-net-20261014`(BUG-1354~1356) |
| `TASK-beta-safety-net-20261014.md` | `PROGRESS-beta-safety-net-20261014.md` | **内测安全网**:每日备份 + 恢复演练、探活告警、本地错误汇总、日志轮转、按 requestId 查日志。发布通路与 #58 已在开工时的 `origin/staging` 上。主机安装和一次演练留给产品 | 待验收 | `codex/beta-safety-net-20261014`(BUG-1355~1357) |
| `TASK-beta-safety-net-fix-20261015.md` | — | **#75 产品选 A**:授权新增每日备份定时工作流,但每日备份不得改写 `recovery-baseline.env`(恢复点仍只手动创建);另记 `daily-backup-latest.env` | 待领取 | `codex/beta-safety-net-20261014` |
| `TASK-beta-capacity-20261014.md` | — | **内测容量**:按用户限流、重计算短排队与对话重试、兑换码当邀请码、staging 10/20 并发压测。**等 #58 合入后开工,压测等 #57 合入** | 待前置 | — |
| `TASK-beta-capacity-fix-20261015.md` | — | **#76 第 1 次打回**:`a54f75e5` 限流与短排队逻辑通过;源码文本断言未跟改、插入新测试使隐私白名单行号错位 | 待领取 | `codex/beta-capacity-20261014` |
| `TASK-special-lagnas-native-20261008.md` | `PROGRESS-special-lagnas-native-20261008.md` | **特殊上升点原生计算(线上缺陷)**:sync6 U6 运行时调 PyJHora,生产镜像没有 PyJHora → staging 上 Hora/Ghati/Sree/Indu/Pranapada 全 blocked;原生实现、PyJHora 只作冻结对照、快速门禁止运行时 import jhora;最先做 | 已验收(已部署 staging `795b8845`,BUG-1273) | `codex/special-lagnas-native-20261008`(未上线;部署以 `/api/health` 为准) |
| `TASK-ephemeris-nutation-20261008.md` | `PROGRESS-ephemeris-nutation-20261008.md` | **恒星黄道少扣章动(BUG-1275)**:行星 = 含章动热带视位置 − 平岁差,全体偏 Δψ(≤17″),大运日期差 2–3 天;改标准算法、PyJHora 冻结对照、scoring-12、v5 重跑 | 已验收(已部署 staging `db0134f9`,BUG-1275) | `codex/ephemeris-nutation-20261008`(未推送,未部署) |
| `TASK-sync6-gate-red-20261007.md` | — | **sync6 后门禁仍红**:run 3213 快速门已过,「Frontend and database tests」开跑 20 秒后日志中断、15 分钟后判失败,后续步骤 failure 0 秒;疑运行机掉线;先 Re-run,红则本机带 Docker 跑 `CI=1 npm test` 对照 `0ed45a4b` | 已完成(Re-run 通过,BUG-1271,部署 `2df6ee72`) | — |
@@ -483,10 +485,10 @@
| `TASK-consult-prep-parallel-fix-20261014.md` | `PROGRESS-consult-prep-parallel-20261013.md` | **#62 第 1 次打回**:两条源码文本检查改认新位置,对照脚本挪进测试目录 | 待验收 | `codex/consult-prep-parallel-20261013` |
| `TASK-frontend-dead-code-20261013.md` | `PROGRESS-frontend-dead-code-20261013.md` | 前端死代码 17 文件与无调用方接口删除,含未登录重计算接口(BUG-1342) | 待验收 | `codex/frontend-dead-code-20261013` |
| `TASK-frontend-dead-code-fix-20261014.md` | `PROGRESS-frontend-dead-code-20261013.md` | **#63 第 1 次打回**:类名白名单去掉已删组件留下的条目 | 待验收 | `codex/frontend-dead-code-20261013` |
| `TASK-api-chart-subprocess-gate-20261013.md` | — | 合盘不带 skip 串行排盘、报告无效子进程、名额轻重混用(只模拟)(BUG-1333~1334,A/B) | 待领取(排队:api-disk-cache-retention + report-reference-perf) | `codex/api-chart-subprocess-gate-20261013` |
| `TASK-api-chart-subprocess-gate-fix-20261015.md` | — | **#64 第 1 次打回**:`336286e4` 内容通过(合盘 1.19→0.015 s、正文逐字节一致);Python 合同按源码检查合盘路由旧写法未跟改;A/B 脚本默认覆盖入库记录 | 待领取 | `codex/api-chart-subprocess-gate-20261013` |
| `TASK-answer-effort-ab-20261013.md` | — | 答题步推理强度开关 + A/B(需模型 key) | 待领取(排队:consult-prep-parallel) | `codex/answer-effort-ab-20261013` |
| `TASK-answer-effort-ab-fix-20261015.md` | — | **#65 第 1 次打回**:`ab7f66be` 开关逻辑通过(默认请求体不变);研究脚本从 `frontend/tests` 读文件(BUG-1140) | 待领取 | `codex/answer-effort-ab-20261013` |
| `TASK-api-chart-subprocess-gate-20261013.md` | `PROGRESS-api-chart-subprocess-gate-20261013.md` | 合盘不带 skip 串行排盘、报告无效子进程、名额轻重混用(只模拟)(BUG-1333~1334,A/B) | 第 1 次打回,见修复单 | `codex/api-chart-subprocess-gate-20261013` |
| `TASK-api-chart-subprocess-gate-fix-20261015.md` | `PROGRESS-api-chart-subprocess-gate-20261013.md` | **#64 第 1 次打回**:`336286e4` 内容通过(合盘 1.19→0.015 s、正文逐字节一致);Python 合同按源码检查合盘路由旧写法未跟改;A/B 脚本默认覆盖入库记录 | 待验收 | `codex/api-chart-subprocess-gate-20261013` |
| `TASK-answer-effort-ab-20261013.md` | `PROGRESS-answer-effort-ab-20261013.md` | 答题步推理强度开关 + A/B(需模型 key) | 待验收 | `codex/answer-effort-ab-20261013` |
| `TASK-answer-effort-ab-fix-20261015.md` | `PROGRESS-answer-effort-ab-fix-20261015.md` | **#65 第 1 次打回**:`ab7f66be` 开关逻辑通过(默认请求体不变);研究脚本从 `frontend/tests` 读文件(BUG-1140),已挪到 `frontend/tests/answer-effort-probe.mts` | 待验收 | `codex/answer-effort-ab-20261013` |
| `TASK-consult-route-split-20261013.md` | — | consult POST 拆分第一阶段:开流前加载 + 计费结算 | 待领取(排队:consult-prep-parallel、answer-effort-ab) | `codex/consult-route-split-20261013` |
| `TASK-pl9-render-split-20261013.md` | — | render_pl9_markdown 第一阶段:搬出无依赖内部函数 | 待领取(排队:api-chart-subprocess-gate) | `codex/pl9-render-split-20261013` |
| `TASK-python-hygiene-20261013.md` | — | 免费层排队开关报反、静默吞异常、功能吉凶兜底合一、测试专用脚本归位(BUG-1340) | 待领取(排队:pl9-render-split) | `codex/python-hygiene-20261013` |
@@ -0,0 +1,37 @@
# TASK · 内测容量修复单(issue #76,第 1 次打回)
- 日期:2026-10-15
- 原任务书:`docs/tasks/TASK-beta-capacity-20261014.md`
- 被验 SHA:`a54f75e5e33775d5dc91f3058f5505a56b498bf2`(`codex/beta-capacity-20261014`)
- 执行方:Grok,同一分支续做(先 merge 最新 `origin/staging`,不改写已推历史)
## 1. 验收结论:逻辑通过,全量多出 2 条失败
| 检查 | 结果 | 证据 |
| --- | --- | --- |
| 按用户限流 | 通过 | 问、校正每轮、报告每小时各有分钟 / 小时两档;任一档满就整体拒,拒绝不记账;都在扣点之前 |
| 重计算短排队 | 通过 | 并发仍是 2(BUG-717);最多 4 个等待、每个最多 8 秒,等不到仍 429(BUG-720 有上限) |
| 前端排队重试 | 通过 | 最多 2 次、总等待 ≤ 15 秒;最终失败走原取消路径 |
| tsc / lint | 通过 | — |
| **前端全量** | **未通过:新增 1 条** | `frontend/tests/consultation-voice-contract.test.ts`「consult user turn no longer dumps tool JSON that the model would parrot」:`/await runConsultationWorkflow\(toolInput/` 不再匹配——`route.ts` 改成了 `await retryOnConsultationRateLimit({ run: () => runConsultationWorkflow(toolInput, …) })` |
| **Python 全量** | **未通过:新增 1 条** | `tests/test_repo_privacy_markers.py::test_tracked_repository_has_no_privacy_markers`:`HOME_PREFIX frontend/tests/personal-report-api.test.ts lines=845,873,903` |
第 2 条不是真的隐私泄漏:这三行是已有的、经审过的虚构路径样本,`tests/test_repo_privacy_markers.py` 的 `SAFE_LINES` 按「文件 + 行号 + 整行」锁定它们。本单在它们上面插入了一条 13 行的新测试,行号整体下移,白名单就对不上了。
## 2. 修法要求
1. `consultation-voice-contract.test.ts`:按 §7.3 写三栏改断言,**意图不放宽**——仍要锁住「工作流调用的入参是 `toolInput`」与该测试原本关心的那段(用户轮不再塞工具 JSON);新正则匹配 `runConsultationWorkflow(toolInput` 即可,不得去掉对 `toolInput` 的锁定。
2. 隐私白名单二选一(推荐 a):
a. 把新加的「hourly rate limit returns 429」测试挪到文件末尾,让那三行回到原行号,`SAFE_LINES` 不动;
b. 更新 `SAFE_LINES` 里这三行的行号,按三栏写原值 / 新值 / 原因。
3. 再 grep 一遍 `route.ts` / `rectification/agent/route.ts` / `reports/route.ts` 被改写语句在 `frontend/tests`、`tests` 里的源码文本断言(§7.8)。
4. 交回前本机跑:`npx tsx --test tests/consultation-voice-contract.test.ts tests/runtime-sources-no-test-imports.test.ts` 与 `python -m pytest tests/test_repo_privacy_markers.py -q`。
5. 业务代码不改。
## 3. 环境缺口(不阻断)
`frontend/scripts/research/beta-load-smoke.mts` 并发压测未跑(无测试验证码、无预发主机)。合入部署后由 Claude 在验收清单里列给产品负责人,或另开一单在 staging 跑。
## 4. 让步顺序
无。第 1 次打回。
@@ -0,0 +1,31 @@
# TASK · 内测安全网修复单(issue #75,产品选 A)
- 日期:2026-10-15
- 原任务书:`docs/tasks/TASK-beta-safety-net-20261014.md`
- 被验 SHA:`53d8df257ddef8918034acef36df0b4b7daade2c`(`codex/beta-safety-net-20261014`)
- 执行方:Grok,同一分支续做(先 merge 最新 `origin/staging`,不改写已推历史)
- **决策记录**:产品负责人 2026-10-15 在 issue #75 选 **A**——**授权新增定时工作流 `.gitea/workflows/backup-production-daily.yml`**(每天北京时间 04:00 自动连生产做加密备份),但**每日备份不得改动「当前恢复点」记录**。恢复点仍只由产品负责人手动点「Create production recovery」创建。仍不改已有四个生产工作流的语义;仍不点任何生产按钮。
## 1. 问题
`deploy/run-production-recovery.sh` 无论由谁调用,结尾都会无条件写 `$DEPLOY_PATH/.state/recovery-baseline.env`(`RECOVERY_REFERENCE`、`RECOVERY_SHA256`、`RECOVERY_FORMAT=2` 等)。发布、迁移、回退都读这份文件当「当前恢复点」。每日备份复用这个脚本,于是每天 04:00 都会把它换成当天的自动备份——产品负责人上线前手动建的恢复点,隔一夜就被替换。
## 2. 修法要求
1. **每日备份不写 `recovery-baseline.env`**:`RECOVERY_BASENAME_PREFIX=production-daily` 时(或新增一个明确的模式参数,二选一,写进 PROGRESS),脚本仍完整执行「导出 → 加密 → 还原核对(含 #74 的表权限、列权限、属主三项比对)→ 写 `*-restore-verify.json`」,但**跳过写 `recovery-baseline.env`**,并在输出里打印 `baseline_updated=false`。手动恢复点路径行为不变,打印 `baseline_updated=true`。
2. 每日备份的核对结果单独记录:在 `$DEPLOY_PATH/.state/` 下另写一个 `daily-backup-latest.env`(只含文件名、sha256、校验时间、格式版本,不含数据、不含密钥),供告警 / 运维查看;发布、迁移、回退工作流**不得**读取它。
3. 合同测试(`tests/test_beta_safety_net.py` 或 `frontend/tests/staging-backend-workflows.test.ts`):
- 每日模式下 `recovery-baseline.env` 不被创建或修改(用临时目录 + 假 docker 跑脚本,断言文件 mtime / 内容不变);
- 手动模式下仍会写,且含 `RECOVERY_FORMAT=2`;
- 三个读取恢复点的生产工作流 / 脚本不引用 `daily-backup-latest.env`。
4. runbook 与 `deploy/README.md`:写清「每日备份只做存档与核对,不是发布用的恢复点;上线前仍要手动点 Create production recovery」。
5. 清理脚本只删 `production-daily-*` 前缀的文件(原任务书已要求)——再确认不会删 `production-pre-migration-*` 手动恢复点。
## 3. 验收
- Claude 会对本单做完整验收(上一轮在发现定时工作流后就停下等拍板,其余部分尚未逐行验):逐行看新工作流与各脚本、跑 `tests/test_beta_safety_net.py`、Python 全量、前端全量、`docker compose config`。
- 交回前本机跑:`python -m pytest tests/test_beta_safety_net.py tests/test_repo_privacy_markers.py -q`、`npx tsx --test tests/runtime-sources-no-test-imports.test.ts tests/staging-backend-workflows.test.ts`。
## 4. 让步顺序
无。
+43
View File
@@ -0,0 +1,43 @@
# 答题步推理强度(2026-10-15)
没有模型钥匙。下面只是本机拦截到的请求体,不是供应商的接受度,也不是耗时和质量。A/B 未跑。
命令:在 `frontend` 目录执行 `node node_modules/tsx/dist/cli.mjs tests/answer-effort-probe.mts --dry-run`。假请求不离开这台机器。模型对象与产品相同:`providerId: deepseek`,`modelId: deepseek-flash`,带 `url` 和假的 `apiKey`。2026-10-15 起脚本在 `frontend/tests/`,不再放在 `frontend/scripts/research/`。
## 请求体里实际有什么
| 形状 | thinking | reasoning_effort | max_tokens |
| --- | --- | --- | --- |
| 今天的答题设置,第 0 步和第 1 步 | `{ type: enabled }` | 无 | 24576 |
| 旧的低强度设置(没有产品调用方) | `{ type: enabled, reasoningEffort: low }` | 无 | 10240 |
| 新开关 `low` / `medium` / `high` / `max`(只在答题步) | `{ type: enabled }` | 与开关相同 | 24576 |
| 续写 | `{ type: disabled }` | 无 | 16384 |
旧的低强度写在 `thinking` 里面,请求体没有 `reasoning_effort`。这证实了任务书 §2.3。记为 BUG-1354,状态 investigating。原先写成 BUG-1351,该号已被数据层占用。线上不用这处设置。
新开关写在供应商选项的最外层。兼容通道把它写成 `reasoning_effort`。开关不设时,两步请求体与改前逐字节一致,样本在 `frontend/tests/fixtures/answer-effort-switch-20261013.json`。
## 供应商接受度
未跑。没有 `DS_KEY` / `BACKTEST_MODEL_KEY`。`low`、`medium`、`high`、`max` 哪几档能用,要等验收时用临时钥匙跑探针的后半段。
## 固定 10 题
脚本已能按 `--cases` 跑下面这 10 题,并能按 `--efforts unset,low,medium` 把同一题的几档挨着跑。多档同时跑时并发降为 1,因为强度读的是进程里的环境变量,并行会串档。本机没有钥匙,这 10 题没有跑。
| # | 盘 | 领域 |
| --- | --- | --- |
| 1 | steve_jobs | career |
| 2 | barack_obama | parents |
| 3 | elizabeth_taylor | marriage |
| 4 | marilyn_monroe | parents |
| 5 | judy_garland | health |
| 6 | edith_piaf | marriage |
| 7 | frida_kahlo | health |
| 8 | george_w_bush | career |
| 9 | zinedine_zidane | career |
| 10 | marilyn_monroe | marriage |
## 耗时与质量
没有数。空答、截断、rubric、人工四项都没做。默认值维持不设。
@@ -0,0 +1,53 @@
{
"charts": {
"serial_seconds_median": 8.0596,
"concurrent_skip_seconds_median": 0.0166,
"serial_subprocesses_median": 2,
"concurrent_skip_subprocesses_median": 0,
"summary_bytes_match": true
},
"report_forced_subprocess": {
"seconds_median": 16.5322,
"subprocesses_median": 7
},
"report_short_circuit": {
"seconds_median": 15.3235,
"subprocesses_median": 1
},
"report_bytes_match": true,
"schemes": [
{
"scheme": "O1",
"consult_429": "0.40",
"report_429": "0.00",
"score_429": "0.17",
"peak": 2,
"touches_bug_717": false
},
{
"scheme": "O2",
"consult_429": "0.00",
"report_429": "0.00",
"score_429": "0.17",
"peak": 3,
"touches_bug_717": true
},
{
"scheme": "O3",
"consult_429": "0.30",
"report_429": "0.00",
"score_429": "0.33",
"peak": 2,
"touches_bug_717": false
},
{
"scheme": "O4",
"consult_429": "0.00",
"report_429": "0.00",
"score_429": "0.67",
"peak": 2,
"touches_bug_717": false
}
],
"jhora_installed": false
}
@@ -0,0 +1,40 @@
# 合盘排盘与报告子进程 A/B(2026-10-15)
Windows 本机,工作树 `.venv` 的 `python.exe`。`importlib.util.find_spec("jhora")` 是 `None`。`python3` 指向 Windows 应用商店占位程序,`python3.14` 不存在。Docker 守护进程在,本机没有 `jyotisha-api` 镜像,所以没有进镜像再探一次。本机没有 VedAstro SDK,外网关闭(`VEDASTRO_ENABLE_NETWORK=0`)。没有「真 SDK」一组。
脚本:`scripts/research/api_chart_subprocess_ab.py`。每组 3 次,取中位数。盘是任务书里的虚构盘(1990-01-01 12:00 与 1992-01-02 12:00,新德里)。星盘缓存 TTL 设为 0。易变字段在比对前丢掉:`generated_at`、`created_at`、`timestamp`、`elapsed_seconds`、`elapsed_ms`、`duration_ms`、`request_id`。原始数字在同目录 `api-chart-subprocess-gate-ab-20261013.json`。
对照不是另一次 `git show` 出 staging 再导入。星盘对照是本分支的 `_compute_chart_sync`:不带 skip、两张盘串行。报告对照是本分支把「参照引擎已安装」强制打开,从而仍会起子进程;静态校验缓存和解释器查找缓存两边都开着。所以报告耗时差只含那 6 个注定失败的回放进程,不含静态校验少跑的那一次。静态校验「连调两次只跑一次」由单测覆盖。
## 合盘两张盘
| | 中位耗时 | 中位 `subprocess.run` 次数 | 盘面摘要 |
| --- | --- | --- | --- |
| 对照:不带 skip,串行 | 8.0596 秒 | 2 | — |
| 本单:带 skip,并发 | 0.0166 秒 | 0 | 与对照逐字节一致 |
0.0166 / 8.0596 ≈ 0.2%,低于「不超过对照 10%」的线。子进程从 2 降到 0。任务书写的「4 → 0」是按每张盘 2 个子进程、两张合计 4 个来估的。这台机器上两张盘合计只数到 2 次 `subprocess.run`。不带 skip 的 8 秒里,大部分是没装 SDK 时子进程空等,不是盘面计算本身。
## 个人长报告(完整数据版,中英,带事实表)
| | 中位耗时 | 中位 `subprocess.run` 次数 | 中英正文与事实表 |
| --- | --- | --- | --- |
| 对照:强制仍起回放子进程 | 16.5322 秒 | 7 | — |
| 本单:没装参照引擎就不起 | 15.3235 秒 | 1 | 去掉易变字段后逐字节一致 |
少 6 次,对应年度回放 3 次和 Tajika 3 次。剩下的 1 次两边都有,短路没有把它去掉。子进程数 1,低于「模拟生产不超过 3」的线。耗时 15.32 秒,不比对照的 16.53 秒差。本机没装 PyJHora,没有再跑「装了参照引擎」的一组;那种环境仍必须走子进程,单测用桩覆盖。
生产上的收益仍按任务书:大约每份报告 0.5 到 0.9 秒,不是装了参照引擎的机器上看到的约 5 秒。
## 名额方案模拟(没改规则)
固定一分钟:2 份报告(每次 13 秒)、20 次咨询(每次 0.2 秒)、6 次校正打分(每次 8 秒)。随机种子 64。并发上限按现状 2。O2、O3、O4 只在脚本里模拟。
| 方案 | 咨询被拒 | 报告被拒 | 校正被拒 | 同时在跑的最高个数 | 是否碰到「不得加池、不得提高并发」 |
| --- | --- | --- | --- | --- | --- |
| O1 现状 | 40% | 0% | 17% | 2 | 否 |
| O2 咨询单独小池 | 0% | 0% | 17% | 3 | 是,本单不实现 |
| O3 咨询最多再等 1.5 秒 | 30% | 0% | 33% | 2 | 否,本单不实现 |
| O4 重任务最多占 1 个名额 | 0% | 0% | 67% | 2 | 否,本单不实现 |
这是一分钟的草图,不是 staging 上的真实频率。咨询少被拒的时候,校正会多被拒(O3、O4)。O2 咨询不再被拒,但同时在跑的个数到了 3,越过了现在的上限。