merge: sync staging task sheet into cross-midnight gate fix

Preserve both the reviewed implementation and latest staging records. No production scoring changes beyond aa46da10.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
jesse-ux
2026-09-20 14:19:17 +08:00
co-authored by Claude Code
34 changed files with 1127 additions and 31 deletions
+27
View File
@@ -12871,6 +12871,33 @@
- 复发自:无
- 修复版本:`8dc460da`
## BUG-976 | 普通对话寒暄被当作咨询:全量排盘、长判词与扣点
- 状态:blocked(独立审查、Linux 全量 3566/3566、标准 DB 40/40 及构建/gzip 通过;staging 部署与真人实测待闭环)
- 首次发现 / 最近更新:2026-09-20。
- 影响面:普通 `POST /api/consult` 三种模式、流式 UI、咨询账务与历史恢复。
- 现象与触发:纯打招呼也进入强制计算与报告形状,并扣一次咨询点数。
- 根因:入口没有非咨询轮,预留与完整咨询链覆盖全部普通消息;原取消函数只退款不持久化回复,没有免费完成语义。
- 修复:所选模型一次短结构化分类并写寒暄回复,3 秒 fail-open,无 Skill/工具/出生资料注入;只看本轮问题、称呼与最后完整可见问答。普通入口命中后先原子免费结算,再发 answer.delta + 无 receipt 的完成事件。新 service_role-only RPC 沿用 advisory lock,校验所属/状态/回复,记录真实 usage、按 credit_amount 退款,完成请求并释放 reservation。完成重试不退款,已收费咨询不能借此退款。UI 标记持久化,寒暄不造步骤/思考/技法。
- 验证:假模型、schema、超时、断连、流协议和 SSR 展示定向通过;真实 PostgreSQL 17 覆盖退款幂等、回复冲突、跨用户/会话、取消竞态、已收费拒退、真实成本、事务回滚和无点数来源。Linux tsc 0、lint 0 error/120 既有 warnings;标准 DB 基线 39/39、候选 40/40,完整构建两侧首页 Static,首屏 JS gzip 621,299 → 621,605 B+0.0493%)。Windows 迁移镜像与 Skill alias EPERM 的历史不删除,Linux 完整全量及部署证据见进度。
- 防复发:保留分类失败回咨询、服务端专有授权、先持久化后完成、刷新标记、取消/完成共享锁回归;不得加入寒暄关键词/正则/字数分类。独立审查补获 SDK strict 校验日志可能携带原文、且非法输出丢 usage 两项,已局部静默 SDK logger 并保留生成结果后严格解析,真实 SDK 假模型回归断言隐私、fail-open 与成本保留;相关定向主会话复跑 59/59,真实库定向复跑 1/1。
- 关联记录:BUG-922 / BUG-923 / BUG-937 / BUG-938;这是非咨询轮的新边界,不是旧每轮排盘防线复发。every turn、prepareStep、contractReady 与两处 requireTool:true 未改。BUG-280/305 的空回答或失败不得结算语义保持;BUG-347 的真实咨询思考历史不删除。
- 边界:reserve 仍在分类前,零点无订阅仍被拦;超时未知 usage 不伪称零,晚 usage 只观测不追写完成账本。
- 修复版本:`539d4dae`,已合入 staging 并部署(`/api/health``deployment.gitCommit``apiGitCommit` 均为该 SHA`latestMigration` = `20260920010000_consultation_free_completion.sql`2026-09-20 由 Claude 独立核对);详见 PROGRESS-consult-smalltalk-fastpath-20260920。状态保持 blocked:真人三模式与余额走查未做。
## BUG-977 | 寒暄提示词豁免只存在于本命 Agent
- 状态:blocked(本地合同通过;真实三模式模型验收待执行)
- 首次发现 / 最近更新:2026-09-20。
- 影响面:productConversationVoice、natalSpokenReportContract,窗口/无出生分钟的简短社交回复。
- 现象与触发:只打招呼也套开场形状;窗口与一般模式拿不到原本命 chit-chat 豁免句。
- 根因:豁免写在仅本命拼接的常量内,共享 voice 的四步形状没有例外。
- 修复:将豁免移到三模式共享 voice,明确只回一句、≤20 字、无句号、无星盘主张;本命领域问题仍需原 opener-plus-skeleton。主修仍是 BUG-976 分流,提示词不代替计费与计算边界。
- 验证:三种指令模板展开共享 voice 后均含豁免,原 OPENER SHAPE 合同保留;未用真实模型假装语义验收。
- 防复发:共享语气规则不能只放单一模式专属合同;混合咨询/解释/纠错/抱怨仍走完整路径。
- 关联记录:BUG-922 / BUG-923 的边界之外,不撤销每轮咨询排盘;与 BUG-976 配套。
- 修复版本:`539d4dae`,已合入 staging 并部署,Skill 版本不变。状态保持 blocked:真实模型三模式语义验收未做。
## BUG-978 | 封存契约当前打分身份与资料审计状态过期
- 状态:resolved
+8
View File
@@ -8,6 +8,14 @@ Purpose: read this file before substantial project work. It exists to stop repea
- 新工作树预检仍为远端 verified、适配器/碎片检查成功、focused 镜像路径断言失败;快速门基线与本轮均在 source inventory 因缺 `mcp` 失败。BUG-089 的 `mcp>=1.0,<2` 约束两处仍在,本机问题是未安装,不是再次升到 MCP 2。
- 广域校正 Python 基线已有 4 个失败,必须逐条对照,不借机修改历史冻结值或削弱旧断言。验收记录见 `docs/tasks/PROGRESS-rectification-cross-midnight-20260920.md`
## 2026-09-20 · 寒暄工作树的同类 Windows 预检缺口
- 实际基线 `fcad06372`pre_work_check 远端 verifiedfocused 23 pass / 1 fail,缺 `.workbuddy` 历史镜像断言;不制造镜像目录。
- junction 依赖导致 Turbopack 越 root,改成本树本地硬链接文件仅只读复用,未 install/patch。构建仍被 live Skill alias symlink EPERM 阻塞,与 09-19 同类;无 Static/gzip 通过结论。
- Docker 实际可用;标准迁移扫描因 Git symlink 被 Windows 检出为普通文本而 duplicate filename。新 RPC 定向验证从本树真实 SQL 内容组成临时迁移集,未改业务迁移扫描器,标准失败另记。不得误写「无 Docker」。
- 同日 Linux 补验解除 DB/build/gzip 缺口:根目录 `git archive` + 本轮文件 overlay,解包到独立 named volume8 个 symlink 保真;容器内按锁文件 `npm ci`。Node 22 Bookworm runner 与独立 Docker 29 DinD 共享网络命名空间、同路径 `/work` volume,内层 daemon 只监听 loopback,无宿主 socket/发布端口;标准 DB 39/39 → 40/40,完整 build 两侧 Static,首页 JS gzip +0.0493%。Windows 原失败仍保留,不修改业务代码掩盖。
- 环境准备防复发:从 frontend 调用 `git archive` 会仅归档该子目录,必须 `git -C <根> archive`DinD 显式传 `dockerd` 再传监听参数,避免入口自动追加 `0.0.0.0:2375` 与自设 loopback 冲突。Node 官方镜像缺 `python3-yaml`/`rsync` 会使 3 个部署合同失败,两侧同名单;只在本轮容器补系统依赖后重跑,不改仓库依赖或 workflow。
## 2026-09-20 · 校正验证补缺的本机验收复现
- 基线与实现工作树均使用 Python 3.11.7(无项目 `.venv``python3` launcher 退出 49)。开工预检远端 verified,但同一碎片镜像路径断言失败;不得把同步成功写成预检通过。
@@ -0,0 +1,98 @@
# PROGRESS · 普通对话寒暄快速通道 · 2026-09-20
## 状态与边界
本地实现和主会话独立审查完成;审查发现的 SDK 原文日志与无效输出 usage 丢失两项已修复并复验。**Linux 前端全量、标准 DB、tsc/lint、完整构建与 Static/gzip 均通过**;真人模型/余额/刷新验收仍缺受控登录态,不宣称已交付线上。分支 `codex/consult-smalltalk-fastpath-20260920`,实际基线 **fcad06372**,不是任务书成文时的 09b41009。用户授权补验后提交并推 staging;当前记录写于提交前,门禁/部署待核验。未 stash,未改 workflow/main/DNS,未读凭据、未打真实模型;所有改动只在授权工作树。
## 实现清单
| 范围 | 实现 |
| --- | --- |
| 分类 | consultation-smalltalk.ts:同一 selectedModel,裸 Agent 无 Skill/tools/memorymaxSteps=1、maxRetries=0、96 输出 token、thinking disabled;本轮可见问题 + 最后完整相邻问答 role/text + 称呼;strict union,≤20 字回复形状;3 秒 Promise.race + abort,任何不确定/错误回 consult |
| 路由 | resolveConsultationQuestion 与预留/问题落盘之后、主 Agent/标题/摘要之前分流;仅 entrypoint undefined;三个特殊入口保持原链(旧校正入口仍拒绝);寒暄不启动标题/摘要模型 |
| 流 | 沿用实际 application/x-ndjson,不另造 SSE 协议;先成功持久化/退款再 answer.delta + run.completed(responseKind=smalltalk),无 activity/receipt;失败只 run.failed,不能声称成功;断连不打断服务端结算 |
| SQL | 仅新增 complete_consultation_free 函数,无表变更;沿用 advisory lock/owner/session/status 校验;complete_usage 写真实成本;按 reservation.credit_amount 退款并记交易;request completed、reservation released,释放订阅 quota;同回复重试成功不退款,不同回复冲突、取消拒绝、收费完成不可退 |
| 权限 | 仅 service_role grant,无 admin_runtime 新 grant,撤销 PUBLIC/anon/authenticated;服务端 createAdminSupabaseClient 使用 service role,客户端 RPC 是 authenticatedresponseKind 自报不产生免费权力 |
| UI | responseKind 从流/header→hook→StreamingReply/ChatMessage→transcript/row→cloud read 贯穿,刷新不重建虚构步骤;寒暄隐藏思考/活动/技法;未有真实活动时不造分类思考;Home 只新增一行 prop0 新 useState/useRef |
| 语气 | 豁免移到共享 productConversationVoice,三模式均含;领域咨询原开场骨架不改,Skill 版本不变 |
| 观测 | classifier 独立封闭机器码、耗时/tokens/按模型价格估算 cost;成功咨询账本合并分类 usage,寒暄记其真实 usage;缓存 normalized usage 合并不丢失;无问题/姓名/出生资料/输出原文日志 |
## 保持的安全合同与产品边界
- BUG-922/923/937/938 及相关历史已检索阅读;最大号 975,本轮新增 976/977。没有修改 index.ts every turn 两句、consultationNatalPrepareStep/WindowPrepareStep、contractReady 或原 stream-agent-response;两处 requireTool:true 保持。
- **预留仍在分类前**。零余额无有效订阅用户仍受前端守卫/服务端 402 限制;没有擅自改计费入口为匿名无限免费模型代理。profile/mode/session 校验也保持。
- 历史可见正文可能含此前解读,任务允许这一对历史;只作为用户语义数据,不是工具/系统星盘证据,提示词禁止复述盘上主张。未声称能力剥夺可数学保证模型零幻觉。
- 订阅无 credit debit 时不加点;reservation released 不再计入 reserved/completed quotaledger 仍有实际成本。定向覆盖无点数来源分支;真实订阅授权链见真人清单。
- 供应商忽略 abort 时最多等待约 3 秒;晚 usage 用 late_usage 观测,不追写已完成/释放 reservation,避免改变状态和重复账务。供应商错误/超时未返回 usage 为 unknown,不伪报真实零成本;价格字段计算是既有模型价格估算,不是供应商账单对账。
- 误判成本按任务书:咨询误判成寒暄会收到一句短回应,需要再问一次;产品接受它比整轮合同失败轻。漏判仍按原咨询处理,宁可多算一次,不用关键词/正则/长度补分类。假模型测试只证明传输/路由/严格解析,不证明真实语义准确率或延迟 P50。
## 验收
日志保存在本树 `artifacts/smalltalk/`,都是本地执行产物,不应整目录提交(含临时迁移副本与虚构 DB fixture 日志)。
| 检查 | 未改基线 | 最终候选 | 结论 |
| --- | --- | --- | --- |
| tsc --noEmit | 0 error | 0 error | 通过 |
| npm run lint | 0 error / 120 warning | 0 error / 120 warning | 无新增 warning,不顺修旧 warning |
| npm test | 3486 tests3409 pass / 77 fail | 3522 tests3444 pass / 78 fail(最终 `npm test -- --test-concurrency=2`) | 既有 77 个失败名称逐项相同;新增 1 个新 DB 测试被同一迁移镜像问题阻塞,不称全绿 |
| 标准 npm run test:db | 39 tests11 pass / 28 fail | 40 tests11 pass / 29 fail | 原 28 个失败名称逐项相同,新增 RPC 测试在迁移阶段被同一 duplicate filename 阻塞 |
| 新功能/适配合同定向 | 无新增用例 | 主会话独立 59/59 | 真实 SDK + 假模型合法/非法/供应商错误、隐私和成本、UI SSR、voice、billing/title/audit 合同 |
| 旧边界相关联合定向 | 基线同类 EPERM | 65 tests64 pass / 1 fail | 唯一 general runtime Skill symlink EPERMBUG-922/923 静态合同通过 |
| 新 RPC 隔离 PostgreSQL 17 | 无新增 RPC | 执行方 1/1,主会话独立复跑 1/1 | 主会话逐文件确认临时迁移集 154 个 SQL 与仓库原文一致,真实 SQL、真实并发连接 |
| Next build | 编译成功后页面收集因 Skill alias EPERM 失败 | 主会话最终编译成功(7.6s),收集 `/api/birth-time-guide` 时同类 symlink EPERM | 未通过,不改 loader 绕过 |
| `/` Static / 首屏 gzip | 无完整产物 | 无完整产物 | **blocked;没有 ±2% 证据** |
| pre_work_check | remote verifiedfocused 23 pass / 1 fail | 未变更 Python | 缺 .workbuddy 镜像断言;不造目录 |
### 定向数据库证据
标准命令扫描两迁移目录时遇到 Windows 检出的 Git symlink 普通文本,报 `duplicate migration filename: 20260814025000_personal_report_document_v2.sql`。未改 runner/旧迁移/既有断言。将两目录真正 SQL 文件逐字节复制到本树 artifacts 临时目录,跳过仅含相对路径的镜像占位文本,重复真实文件必须内容一致,共 154 个 SQL;用已有 MIGRATIONS_DIRECTORY 参数运行同一新 DB 测试。
覆盖:service_role 可调用,anon/authenticated/app_runtime 不可;精确退 3 点不是写死 1;同请求重试仅一次退款/一条回复;不同回复冲突;他人 user/session 拒绝;取消后拒绝;普通已完成收费咨询拒退;23 input / 7 output / 91 microusd 实际入账;负 token 触发事务回滚后重试成功;无点数来源不加点且释放计数;两个 PostgreSQL 连接并发 free/cancel,余额守恒且只有一个 refund。并发测试不是生产订阅链或 staging 验收替代。
### 断言调整三栏
| 原值 | 新值 | 原因 |
| --- | --- | --- |
| usagePayload await usage | await mergeUsage([usage, classificationUsage]) | 必须等待并计入分类成本,不弱化完成时机 |
| 标题测试切片截止 usageStartedAt | 截止 usagePayload | 计时前移到分类前;原 expectedTitle/所有权/CAS 断言保留 |
| auditRows 直接 receipt.techniqueAuditTable(两测试) | smalltalk ? undefined : 原字段 | 寒暄不得显示技法;咨询字段不变 |
| completed.receipt 隐式必有 | assert.ok(receipt) 后相同 domains 深比较 | 新协议允许寒暄无 receipt,旧咨询仍显式必须有 |
一次编辑脚本在 Windows 写入 CRLF 导致源码逐字合同额外失败,已恢复被改文件原 LF,不修改业务或放松相关断言来掩盖。新 SQL 测试在标准命令中也被同一个既有 duplicate migration 阻断,会比基线多一个环境失败,不能宣称失败总数完全不变。
## 主会话独立审查与最终证据
- SQL、RPC 角色、迁移打包/加载静态复核未发现确认缺陷;真实数据库独立回归通过,不等于标准整套 DB 通过。
- 修复审查发现:Mastra 默认 strict 校验会把非法模型输出附入 SDK 日志,同时在读取 usage 前抛错。现对独立分类 Agent 与结构化输出处理器使用 `noopLogger`,结构化生成采用保留结果的 `errorStrategy: warn`;外层原有 strict schema 仍是唯一接纳门。真实 SDK 内存假模型覆盖合法输出、非法 schema、坏 JSON、供应商异常;验证一次调用、无工具、96 token cap、原文不进 console/stdout/stderr、非法输出仍保留 23/7 tokens。
- 最终证据:`review-final-targeted.log`59/59)、`review-final-full-concurrency2.log`3522/3444/78)、`review-final-failure-comparison.json`(全量与 DB 逐项名单)、`review-final-tsc.log`0)、`review-final-lint.log`0 error/120 warnings)、`review-final-db-targeted.log`1/1)、`review-final-build.log`EPERM)。
- 原默认并发多次出现内存分配失败,导致部分测试未完整收集;这些失败日志保留,不用作最终测试总数。中途构建还出现 `.next` rmdir UNKNOWN;最终单独复跑重新到达编译成功后 Skill symlink EPERM。未改业务、断言或依赖去迎合环境。
- 执行与审查子代理最后因服务端 API 403 insufficient balance 中断;主会话接手完成上述复验与记录,未将中断代理的待完成工作冒充结果。
- 本轮执行期间远端引用前进至 `5049e784`,相对基线仅新增另一份任务书及索引。未在脏工作树拉取;未来提交/推送前必须重新 fetch、核对 BUG 编号和整合任务索引,不能直接覆盖他人文档。
## Linux 隔离补验(用户授权继续,2026-09-20)
- 主机 Docker Desktop4 CPU / 8 GB;使用本轮专属 runner、DinD 和 named volume,不挂宿主 Docker socket,不访问其他会话容器或凭据。Node `22-bookworm`(拉取 digest `sha256:dd5847a04b0deee391fa145f1f4c6d214196668b6bcc7988ebed67249f226844`),容器内按未修改的 package-lock 执行 `npm ci`;两侧依赖副本相同,不复用 Windows node_modules。
- 基线从根目录 `git archive fcad06372`;候选为相同 archive + 33 个本轮变更文件。8 个 Git symlink 均保真,overlay 全部逐字节匹配工作树,输入哈希清单 `artifacts/smalltalk/linux-input-manifest.json`。为 Git 文件跟踪合同建立仅容器内使用的临时 Git 快照,非发布提交。
- runner 与专属 DinD 共享网络命名空间和 `/work` volume`DOCKER_HOST` 仅 loopback,无发布端口,`TMPDIR=/work/tmp`fixture 上限 1;真实 PostgreSQL 17、标准双迁移目录,无 MIGRATIONS_DIRECTORY 绕行。
| 检查 | Linux 基线 | Linux 候选 | 结论 |
| --- | --- | --- | --- |
| tsc | 0 error | 0 error | 通过 |
| lint | 0 error / 120 warnings | 0 error / 120 warnings | 无新增 |
| 首轮全量 | 3530 / 3527 pass / 3 fail | 3566 / 3563 pass / 3 fail | 失败名称逐项相同:缺 python3-yaml 一项、rsync 两项;原日志保留 |
| 补齐系统依赖后最终全量 | **3530/35300 fail/skip** | **3566/35660 fail/skip** | 新增 36 项,0 新红;`npm test -- --test-concurrency=2` |
| 标准 test:db | 39/39 | 40/40 | 通过,新增免费结算真实库回归包含在内 |
| npm run build | 退出 0`○ /` | 退出 0`○ /` | 首页 Static 不变 |
| 首页 JS gzip | 621,299 B / 25 文件 | 621,605 B / 25 文件 | +306 B / +0.0493%,在 ±2% 内 |
- gzip 口径:各自全新 `.next``.next/server/app/index.html` 中去重 `/_next/static/**.js` 引用,每文件 gzip level 9 后求和;不混入其他路由、旧 hash chunk 或 CSS。复算脚本与逐文件清单在本地 artifacts,不整目录提交。
- 日志:`artifacts/smalltalk/linux-logs/{base,proposed}-{tsc,lint,test,db,build}.log``*-gzip.json`。脚本末尾退出 0 不能代表全部测试成功,逐项结果按表中原始日志记录。
- Gitea REST Actions API 未认证为 401;公开 `/root/Jyotisha/actions` 网页可读,可在不读取凭据的前提下核对门禁。推送前 staging health 为 `12fdaed90b81014378fe5eaa9231e50d2b838a37`,不是本轮部署证据。
- 远端期间另有校正研究提交 `932f2fff`(BUG-978~980),本轮编号无冲突。提交后只整合本分支,保留双方共享文档;不操作他人工作树、不 stash、不提升 main。
## 剩余未做 / 交接
1. 独立代码审查与 Linux 平台验收已完成;最终日志 `linux-logs/{base,proposed}-test-final.log`,机器对照 `final-test-comparison.json`。隐私扫描 `commercial_privacy_artifact_scan.py --json` 为 pass、0 finding3188 文件)。
2. 提交后整合最新 `origin/staging`、快进推送、核对远端 SHA 与 Gitea 门禁/自动迁移/部署。远端至 `03cba478` 额外两提交仅跨午夜任务书与索引,需保留并行工作。
3. 受控 staging 账号三模式真实模型语义、延迟、零点/订阅、取消/断线/刷新与余额走查,清单 `docs/testing/consult-smalltalk-fastpath-20260920.md`
4. BUG-976/977 不提前标 resolved。没有获得真实模型 P50,不按猜测降低 3 秒上限。
+3 -2
View File
@@ -289,9 +289,10 @@
| — (产品口头拍板,无任务书) | `PROGRESS-agent-voice-20260917.md` | **对话口气改形状(纯提示词与文案,无 BUG 号)**:产品判定现有人设出来的是顾问报告。人设改成「把人当一个人认真对待 / 行动力很强、嘴有点毒但靠谱的同事」;开场从「一句结论 + 2–3 条短要点 + 一句下一步」换成固定形状——**反差**(表面 A 底下 B,命名成一个格局)→ **谁在推谁在修**(大运主星推、行运修体面)→ **别去应 X 的象,去扮演 Y 的象** → **最多三条短行动**(各 ≤ 20 字),仍无标题、≤ 400 字。新增希望纪律:有转机且允许精确应期就说到月份,没有就说这段时间拿来干什么、能扮演哪个象,禁「一切都会好 / 相信自己 / 加油 / 你值得更好的 / 宇宙自有安排」。申报时段与无出生分钟两条降级路线形状照给、不编月份。改 `product-voice.ts` / `consult/route.ts` 用户回合文案 / 三处复述旧形状的系统指令 / `VOICE.md`(五条原则→七条)/ `CHANGELOG.md`。零业务逻辑改动,Skill 版本不变 | 已实现,待验收 | `codex/agent-voice-20260917`tsc 0 / lint 0 error118 warning 不变)/ npm test 3468→3471 条、36 红与基线 `ff0427cf` 逐条相同、0 新红 / `○ /` Static / 首屏 gzip 1,416,965 两侧**字节相同**(改动全在服务端模块,客户端 chunk 无该文本)。真机口气走查欠 |
| — (产品口头拍板,无任务书) | `PROGRESS-settings-ui-20260919.md` | **设置面板布局与资料入口整理**:基线 `4f4cd684`;四分区继续共用固定 `.settings-modal`,桌面导航 176px→200px 并加分隔,内容区增加内边距,表单 cap 440px→560px,导航移除误导性右箭头;账户头像 48px→56px;“添加其他人”移到分组标题操作区。已同步 `frontend/DESIGN.md` 与合同测试;tsc 0、lint 0 error、定向测试 21/21build 被 Windows Skill runtime symlink 权限阻塞,浏览器走查待受控环境;BUG-970 保持 `investigating` | 待验收 | `codex/settings-ui-20260919` |
| `TASK-owner-case-purge-20260919.md` | `PROGRESS-owner-case-purge-20260919.md` | **上游库主案例与本机路径残留清除(只含本仓)**:镜像同步带进库主本人案例(敏感案例标识与本机路径)并被 `origin/staging` 命中,涉及无引用的 `versions/` 三快照、前端 fixture、Python 测试、整机扫描台账、会话转录及上游 SKILL 快照。运行时无特判不用动。产品拍板整体删除不留匿名版;fixture 统一虚构常量;`import_yinduzhanxing.py` 加隐私排除项 + 新增仓库级隐私守卫测试;上游 SKILL 快照等库主清完再重导入(BLOCKED 记录)。上游仓的清理指令另见 `UPSTREAM-INSTRUCTION-owner-case-purge-20260919.md`(交给库主,不在本仓执行)。BUG-972/973 | **已验收通过(2026-09-20`497798ac`);未合入 staging,等产品放行** | 两轮:首轮未通过(1 个隐私守卫冲突:新增路径规则与答案键守卫冲突,该守卫在快速门 glob 内,合入会让门禁红)→ 修复单 `TASK-owner-case-purge-fix2-20260919.md` → `497798ac` 通过。Claude 在 Linux 全依赖环境独立复验:快速门 Python 步 859 passed / 0 failed(上一轮就是这步红),Python 全量 63 红与基线逐条相同、0 新红,收集数与删除清单已记录;tsc 0 / lint 0 error、120 warning 同基线 / `npm test` 失败清单为基线子集(少 1 条工作流 YAML,非回归)/ `○ /` Static、首屏 gzip 与基线字节相同(前端自首轮提交起零改动)。上游指令文件已逐字节还原为基线原文;12 个已删测试名与计数已落进度记录;BLOCKED 两条已划掉。校正 Skill 哈希包、注册表、上游快照、`frontend/src`、主 API 全程 0 改动;库主本机用户名 0 命中。遗留 P3:进度记录和 BUG 状态文字待后续对账修正 |
| `TASK-consult-smalltalk-fastpath-20260920.md` | | **普通对话寒暄轮快速通道**:真机一句「你好」触发完整窗口排盘(活动面板「已完成 4 步」)+ `## 先回答你的问题` + 400 字判词 + 扣 1 点。三层叠加:`index.ts` 两处「every turn 必调排盘」(BUG-922)与 `contractReady()` 的 `requireTool` 把排盘变成硬合同;`product-voice.ts` OPENER SHAPE 标「三种模式共用」;唯一的 chit-chat 豁免句只在 `natalSpokenReportContract` 里、只拼进本命 Agent(BUG-977)。计费侧「写回复」与「扣点」绑在 `complete_consultation_response` 同一次调用,`cancel` 只退款不写消息,所以今天没有「不扣点但保留对话」的通道。**产品拍板 a:不扣点、不排盘、回一句白话**;**分流不得用正则/关键词/长度阈值**,改为进 Agent 之前一次极短的结构化模型调用(复用本轮已选模型),fail-open 一律落回完整路径;BUG-922/923 的三处合同一个字不改;新增 `complete_consultation_free` 迁移。BUG-976/977 | 待领取 | — |
| `TASK-consult-smalltalk-fastpath-20260920.md` | `PROGRESS-consult-smalltalk-fastpath-20260920.md` | **普通对话寒暄轮快速通道**:真机一句「你好」触发完整窗口排盘(活动面板「已完成 4 步」)+ `## 先回答你的问题` + 400 字判词 + 扣 1 点。三层叠加:`index.ts` 两处「every turn 必调排盘」(BUG-922)与 `contractReady()` 的 `requireTool` 把排盘变成硬合同;`product-voice.ts` OPENER SHAPE 标「三种模式共用」;唯一的 chit-chat 豁免句只在 `natalSpokenReportContract` 里、只拼进本命 Agent(BUG-977)。计费侧「写回复」与「扣点」绑在 `complete_consultation_response` 同一次调用,`cancel` 只退款不写消息,所以今天没有「不扣点但保留对话」的通道。**产品拍板 a:不扣点、不排盘、回一句白话**;**分流不得用正则/关键词/长度阈值**,改为进 Agent 之前一次极短的结构化模型调用(复用本轮已选模型),fail-open 一律落回完整路径;BUG-922/923 的三处合同一个字不改;新增 `complete_consultation_free` 迁移。BUG-976/977 | 本地实现及独立审查完成,两项 SDK 隐私/成本问题已修复;Linux 最终全量 3530/3530 → 3566/3566、标准 DB 39/39 → 40/40、tsc 0、lint 0 error/120 既有 warning;两侧首页 Static,首屏 gzip +0.0493%**已验收通过(2026-09-20Claude 独立复验 `539d4dae`**:同机两侧对照 tsc 0 / lint 0 error·120 warning / npm test 基线 3530 与候选 3563 失败名单逐条相同(31 条均为无 Docker)/ 两侧 `○ /` Static、首屏 gzip +299 B+0.0483%/ 部署 SHA 与迁移已核对;红线四文件零改动。剩真人模型语义、延迟 P50、余额与刷新走查(无受控账号) | `codex/consult-smalltalk-fastpath-20260920`;实际基线 `fcad06372`;逐项测试对照见 PROGRESS,真人清单见 `docs/testing/consult-smalltalk-fastpath-20260920.md` |
| `TASK-rectification-validation-integrity-20260920.md` | `PROGRESS-rectification-validation-20260920.md` | **生时校正验证体系补缺(纯离线评测,不改打分不改产品)**:会议要求把「推断真实出生时间」与「用户认可的参考盘」分开证明。核对结论——**产品口径侧四条已落地**(`accepted`≠`confirmed` 两条写入路径、确认门 fail-closed 且 `holdout` 为 `not_ready` 使 `confirmation_allowed` 不可能为真、采用不写 `reported_birth_time`、无任何把采用率当准确率的指标;运行时也无按生日走捷径的分支);**缺口全在评测本身**。三条:① 封存契约 `rectification_sealed_holdout.v1.json` 的三个打分哈希互不相同(封存 `f41c298d` / 契约记录 `99730c84` / 基线实测 `b15d9ea1`),`official_eval_trial_count: 0`——当前实现**从未产出过一次有效官方盲测**,唯一跑过那次已被资料审计作废(top-1 `0.15`,发布门要 `0.60`),可见的 `0.45` 自带「不得当发布指标」标记(BUG-978);② 全部离线评测的候选窗**以真值为圆心**(`_candidate_moments()`、`request_from_case()` 的 `true_time`),生产以申报时间为圆心(`ENGINE_SEARCH_RADIUS_MINUTES = 15`)——「真值掉出窗外」这一失败模式从不可见(BUG-979);③ v3 封存但每例仅 3 事件、v4 有 7+ 事件却已被看过并用于调参,**无口径干净又贴近真实会话的封存集**;六题回放的 `0.80/0.55/0.35` 是「真值方向最优答」的上帝视角上界,±30/±60 仍低于发布门(BUG-980)。T1 申报偏差敏感性 sweep、T2 有效重跑 + 契约对齐 + **防复发新测试**、T3 只出 v5 采集协议、T4 记录。**硬红线:不得改 12 个打分文件、不得用封存集调参、不得把 `status` 改 `ready`。** 家庭信息(父母职业/兄弟姐妹)在拿到基线数字前不开工——现有七领域全是带日期事件,静态属性没有输入口。前置:owner-case-purge 三提交仍未合入 staging。BUG-978980 | **已验收通过(2026-09-20Claude 独立复算)**;独立盲测仍 blocked;门禁/部署待核验 | `932f2fff`。Claude 独立复核:T1 完整重跑 900 组合**逐例 8 字段 0 差异**、summary/specification 全等,另从 900 条逐例重算 45 格表与发布表逐位相同;T2 独立复跑 0.45/0.50/6.45/0/1.0/0.0 与哈希全序 0.05/0.10 全部一致;12 个打分文件字节未动、`status=not_ready`、`confirmation_coverage_rate=0`、实跑 `holdout_passed()`=False;新增/触及 Python 测试本机 38 条全绿;freshness 测试比对时排除 `frozen_at_utc`BUG-693/694 教训已用上)。**执行方正确推翻任务书 T2.3**:「首次口径干净的官方盲测」与 BUG-428 防复发(已看过结果的案例不得再计入盲测)冲突,已核 BUG-428 原文,是任务书写错。环境缺口:本机 frontend 无 node_modulestsc/lint/npm test/build 未能复核,采信执行方自报。遗留 → `TASK-rectification-cross-midnight-dasha-20260920.md`BUG-981 |
| `TASK-rectification-cross-midnight-dasha-20260920.md` | `PROGRESS-rectification-cross-midnight-20260920.md` | **跨午夜候选的 Dasha 边界错一天(生产打分)**:`scoring_service.py` 调 `merge_transition_proximity()` 只传**一个** `birth_date`,该函数用它算全部候选的 Vimshottari / Narayana 起始日期,候选之间只靠 `_context_time()` 的 `HH:MM` 区分、**日期被丢掉**。窗口跨午夜时午夜后候选的 dasha 边界整体错一天。Claude 验收时在生产调用链独立复现:窗口 `23:50→00:10`、21 个候选,**恰好那 11 个跨日候选分数错、10 个同日候选逐位相同**(幅度 +0.0267 / −0.0133,本例头名未变)。算术上界 = `cap/kernel_width`day 精度 0.067 分/件,18 件可累计约 1.2 分,而随分钟变化项总量仅约 2.1 分 —— **上界是推的不是实测,真实幅度本单必须实测**。踩中路径:`late_night` 时段 `23:0003:59`、`unknown` `00:0023:59`、23:45 后或 00:15 前申报的 ±15 窗。连带发现:`calculation_spec()` 不含打分实现身份,修复后同一 spec hash 对应不同分数,历史 Case 静默失去可复现性(同 BUG-427 类型)。**产品 2026-09-20 已就三点拍板:A 修、B 修完重新冻结并重跑 T1/T2、C 让新旧结果可区分。** C 的做法经查证已修正:`calculation_spec_hash` 全在 **V4** 链路、**V9 零引用**,原提案 bump `INPUT_CONTRACT_VERSION` 对真实历史无效已作废;改为随修复 bump `engine_version`执行查证:默认串主要写 started/failed成功回执及已有 minute 缓存门实际使用后端算法身份,故同步 `ALGORITHM_VERSION`不新增历史打开门;旧“全仓只写不比假设作废),**不得动 `skill_version`**BUG-621open RPC 要求绑定 Skill 等于当前版本,bump 会让历史校正打不开)。硬红线:只改「按候选日期取 dasha 起始」,不得动 kernel/cap/share 任一常数;确认门不变。BUG-981 | **已实现核心修复,整体验收未通过;独立分支交付供远程 review,不合入 staging** | 基线 `03cba478`;日期红绿、同日逐位、性能与900/20重跑完成;成功回执/缓存实际使用算法身份,已同步后端与前端标记,但独立审查发现BUG-984时段旧缓存与聚合来源未闭环,T4不能全通过。BUG-982/983另登记既有跨日缺口;补单与环境对照见进度。 |
| `TASK-rectification-cross-midnight-dasha-20260920.md` | | **跨午夜候选的 Dasha 边界错一天(生产打分)**:`scoring_service.py` 调 `merge_transition_proximity()` 只传**一个** `birth_date`,该函数用它算全部候选的 Vimshottari / Narayana 起始日期,候选之间只靠 `_context_time()` 的 `HH:MM` 区分、**日期被丢掉**。窗口跨午夜时午夜后候选的 dasha 边界整体错一天。Claude 验收时在生产调用链独立复现:窗口 `23:50→00:10`、21 个候选,**恰好那 11 个跨日候选分数错、10 个同日候选逐位相同**(幅度 +0.0267 / −0.0133,本例头名未变)。算术上界 = `cap/kernel_width`day 精度 0.067 分/件,18 件可累计约 1.2 分,而随分钟变化项总量仅约 2.1 分 —— **上界是推的不是实测,真实幅度本单必须实测**。踩中路径:`late_night` 时段 `23:0003:59`、`unknown` `00:0023:59`、23:45 后或 00:15 前申报的 ±15 窗。连带发现:`calculation_spec()` 不含打分实现身份,修复后同一 spec hash 对应不同分数,历史 Case 静默失去可复现性(同 BUG-427 类型)。**产品 2026-09-20 已就三点拍板:A 修、B 修完重新冻结并重跑 T1/T2、C 让新旧结果可区分。** C 的做法经查证已修正:`calculation_spec_hash` 全在 **V4** 链路、**V9 零引用**,原提案 bump `INPUT_CONTRACT_VERSION` 对真实历史无效已作废;改为随修复 bump `engine_version``v9EngineVersion()` 缺省串用于 started/failed成功回执 minute 缓存身份实际来自后端;已同步 `ALGORITHM_VERSION`旧「只写不比假设作废),**不得动 `skill_version`**BUG-621open RPC 要求绑定 Skill 等于当前版本,bump 会让历史校正打不开)。硬红线:只改「按候选日期取 dasha 起始」,不得动 kernel/cap/share 任一常数;确认门不变。BUG-981 | **已 review:核心修复通过,但暂不合入** —— 阻塞于 `TASK-rectification-cross-midnight-gate-fix-20260920.md`BUG-985 | 实现 `aa46da10`(分支 `codex/rectification-cross-midnight-20260920`,未合入 staging)。Claude 独立 review:修复后整窗打分与**逐候选独立重算 21/21 一致**;非跨午夜分数基线 vs 修复**逐位不变**(我测半径 10 六例 + 半径 60 三例,执行方证据覆盖 19 例 / 2299 候选全部 `bytes_equal`);kernel/cap/share/`PRECISION_WEIGHTS` 一个未动;`status=not_ready`、coverage 0、`holdout_passed()` False、官方试次 0;决策 C 执行正确(`ALGORITHM_VERSION` 7→8`INPUT_CONTRACT_VERSION` 与 `skill_version` 未动,golden 里 `calculation_spec_hash` 不变可证);既有断言改动均带三栏说明且为加强。**P1 阻塞**:新增的 `test_same_day_public_aa_scores_keep_pre_fix_bytes` 写死 121 个浮点分数的 SHA-256,在 Claude 机器上 ordinal 2/3 红、**基线与修复分支同样红**(非修复所致),且经 `test_rectification_cross_midnight_gate.py` re-export 落入快速门 glob —— 同 glob 基线 214 passed/0 failed、`aa46da10` 4 failed,推 staging 会让门禁红。执行方自报未通过的 BUG-984 经独立确认成立且更重:`block_scan` 只比 evidence 指纹不读打分身份,而 `late_night`(299 min)/`unknown`(1439 min) 均 >120 走该分支、又恰是最易跨午夜的窗口 |
| `TASK-rectification-cross-midnight-gate-fix-20260920.md` | — | **review 修复单:门禁级浮点哈希断言(`aa46da10` 的合入阻塞项)**:新增回归把 121 个分数的 SHA-256 写死成字面量,于是断言范围把跨机 libm/pyswisseph 差异也圈了进去。实测该组 121 个分数**全无浮点尾噪**(`repr(s)==repr(round(s,4))` 121/121),所以差异是第 4 位真的不同,靠改序列化消不掉。同一提交刚改过的 `test_rectification_engine_memoization.py` docstring 正好写着「跨机舍入已漂 1.1e-3,不得整体 `==` 比较」——教训被引用了又被踩。执行方自己的证据 JSON 用的却是正确做法(`scores_bytes_equal` = 同机基线 vs 当前)。**根因**:要证的是相对不变量(同机基线 vs 当前),却实现成绝对不变量(当前 vs 某台机器的历史哈希)。F1 改用生产代码已有的 legacy 回退路径做同机 A/B 对照(去掉 `candidate_at` 即修复前行为),验收要求**两台浮点环境不同的机器各跑一次**、且回退核心修复后该测试仍绿(证明它守的是同日不变性而非跨午夜回归的替身);F2 bridge 重复收集(4 failed = 2×2);F3 查证 `block_scan` 重算是否经过被修 helper —— 会则 BUG-981 在 late_night/unknown 路径等于没上线、BUG-984 升为阻塞项;F4 记录。**硬红线:不得删测试/skip/把哈希改成本机当前值消红,不得改已验收的打分代码。** 串行:本单 → `aa46da10` 合入 → BUG-984 补单。BUG-985 | 待领取 | — |
### 跨午夜执行轮独立验收补单
@@ -0,0 +1,165 @@
# TASK · 跨午夜修复 review 修复单:门禁级浮点哈希断言(2026-09-20)
> 状态:待领取。**这是 `aa46da10` 的合入阻塞项**:核心修复本身已验收通过,卡住的是随它新增的一条测试会让快速门在别的机器上红。
> 本单只改测试断言方式,**不改被修的打分代码**。
## 0. 基线与交付
- 基线:分支 `codex/rectification-cross-midnight-20260920` = `aa46da10`(不是 `staging`;本单改的是该分支上的新增测试)。
- 参照:`origin/staging` 在 review 时为 `f8e40e5d``aa46da10` 的分叉点是 `03cba478`
- worktree `.worktrees/rectification-cross-midnight-gate-fix-20260920`,分支从 `aa46da10` 起,或直接在原分支上追加提交。
- **合入顺序**:本单修完 → `aa46da10` 方可快进推 `staging` → 之后才做 `TASK-rectification-cross-midnight-dasha-fix-20260920.md`(BUG-984 缓存身份补单)。三者串行,不得并行改同一批测试文件。
## 1. 事故实证
Claude 2026-09-20 在 `aa46da10` 上独立 review,以下全部为实测。
### 1.1 断言写法
`tests/test_dasha_transition_proximity_cross_midnight.py``test_same_day_public_aa_scores_keep_pre_fix_bytes``hashlib.sha256(_canonical(scores)).hexdigest()` 把 121 个分数的 canonical JSON 哈希**写死成字面量**,三个 ordinal 各一个。
### 1.2 实测:该测试在别的机器上红,且与修复无关
| 运行环境 | 结果 |
| --- | --- |
| `aa46da10`(修复分支),Claude 本机 | `ordinal 2``ordinal 3` **红** |
| `03cba478`(修复前基线),把同一测试文件拷过去跑 | `ordinal 2``ordinal 3` **同样红** |
两侧红的是同一组,**说明不是本轮修复造成的**,是跨机浮点差异。
进一步用同机对照确认修复无辜:以该测试自己的 `shifted_window(case, 0, 60)` 口径,在基线与修复分支各算一次 121 个分数——
| ordinal | 基线哈希前 16 位 | 修复分支哈希前 16 位 | 是否相同 |
| --- | --- | --- | --- |
| 1 | `5e47d5b17c9534f1` | `5e47d5b17c9534f1` | 是 |
| 2 | `b371f6430c7adfc8` | `b371f6430c7adfc8` | 是 |
| 3 | `3fb2b626d86eb7c3` | `3fb2b626d86eb7c3` | 是 |
**同日分数在基线与修复之间逐位不变**(另经半径 10 的 6 例独立复核,同样 6/6 相同;执行方自己的 `docs/testing/rectification-cross-midnight-scoring-evidence-20260920.json` 覆盖 19 例 / 2299 候选,全部 `scores_bytes_equal: true`)。红的原因只是本机算出的字节与写死的字面量不同。
### 1.3 影响面:它在快速门里
`tests/test_rectification_cross_midnight_gate.py` 把该用例 re-export,文件名命中 `scripts/run_quality_gate.py``CORE_PYTEST_TARGETS` 中的 `tests/test_rectification_*.py`。实测同 glob
| 分支 | 结果 |
| --- | --- |
| `03cba478` | **214 passed / 0 failed** |
| `aa46da10` | **4 failed**(2 条 × 2bridge 重复收集) |
即:推 `staging` 会触发 `backend-quality-gate`,而该门在浮点行为与执行方机器不一致的任何机器上都会红。
### 1.4 差异不是 repr 噪声,是第 4 位真的不同
实测该组 121 个分数全部满足 `repr(s) == repr(round(s, 4))`,**0 个带浮点尾噪**。所以哈希不同意味着至少一个分数在第 4 位小数上真的不同,属跨机 libm / pyswisseph 层面的差异,不是 JSON 序列化噪声。这类差异无法靠「多取几位」或「统一序列化」消除。
### 1.5 同一提交里引用了这条教训,又踩了它
`tests/test_rectification_engine_memoization.py` 的 docstring(本轮刚被同一提交修改过)明写:跨机 libm / pyswisseph 舍入**已经**让 `margin_percent` 漂移 `1.1e-3`,因此**不得**对整份 payload 做 `==` 比较。写死浮点分数的 SHA-256 比整份 `==` 更严格。
### 1.6 执行方自己的证据文件用的是正确做法
`docs/testing/rectification-cross-midnight-scoring-evidence-20260920.json``same_day.comparisons` 每条记的是 `scores_bytes_equal` / `matrix_bytes_equal`——**同一台机器上基线与当前的对照**,而不是跨机硬编码哈希。正确做法已经在仓库里,只是没有用在测试断言上。
## 2. 根因
这条回归要证的是「修复不改同日分数」,那是一个**相对**不变量:同机、同环境下,基线行为与当前行为一致。但它被实现成了**绝对**不变量:当前行为等于某一台机器在某一时刻算出的字节。绝对写法把 libm / pyswisseph 的版本差异一并纳入了断言范围,于是换机器就红,而红的信息与要守护的性质无关。
## 3. 决策记录
- **2026-09-20 产品要求就本项出修复单。**
- 本单只改测试断言方式。`aa46da10` 的核心修复(`dasha_transition_proximity.py` 取候选日期、缓存键带日期、`scoring_service.py` 版本号 7→8)已由 Claude 独立验收通过,**不在本单改动范围**。
- BUG-984(时段缓存不校验算法身份)另有补单,串行在本单与 `aa46da10` 合入之后。
## 4. 硬红线
1. **不得用消红代替修复**:不得删除该测试、不得 `skip` / `xfail`、不得把哈希改成「本机当前算出的值」——最后一种只是把红转移给下一台机器。
2. **不得改** `scripts/rectification/dasha_transition_proximity.py``scripts/rectification/scoring_service.py`。核心修复已验收通过(§1.2)。
3. 不得动打分常数、确认门、Skill 版本、`INPUT_CONTRACT_VERSION`、已封存的历史哈希。
4. **不得因为改测试而降低覆盖**:「同日不变性」这条回归必须仍然存在,且仍然留在快速门里。
5. 处理 bridge 重复收集时,不得让原测试脱离快速门 glob。
6. 不顺手升级依赖、不修不在本单内的 warning。
## 5. 任务分解
### F1 · 把绝对哈希换成同机相对比对(BUG-985)
**推荐做法**:用生产代码里**已经存在**的 legacy 回退路径造出「修复前行为」,在同一次运行内对照。
`merge_transition_proximity()` 对缺少 `candidate_at` 的上下文仍回退到请求的 `birth_date`(即修复前的行为,该分支本轮已保留并有专门测试 `test_legacy_context_without_candidate_at_retains_request_date` 覆盖)。因此同日窗口下:
- A 组:正常 contexts(带 `candidate_at`,走新路径)
- B 组:同一批 contexts 去掉 `candidate_at`(走 legacy 路径 = 修复前行为)
- 断言 **A 组分数与 B 组分数逐位相等**
这条断言在任何机器上都成立(因为同日窗口下候选日期本就等于 `birth_date`),且直指要守护的性质,不含任何跨机字面量。
**备选做法**(若 A/B 构造受限):改为结构性断言——同日窗口下,`_vim_start_dates` / `_narayana_start_dates` 收到的日期参数集合恰为 `{birth_date}`。该文件已有同型写法(`test_every_candidate_uses_own_date_and_caches_do_not_cross_dates` 用的就是捕获调用参数)。
验收标准:
- 三个 ordinal 全绿,且**在至少两台浮点环境不同的机器上各跑一次**——执行方自己的机器 + 一台 Linux 全依赖环境;两侧都绿才算通过。做不到两台就在进度记录写成环境缺口,不得写成通过。
- 把核心修复临时回退(令 `candidate_date` 恒等于 `birth_date`)后,该测试**仍然绿**——证明它守的是同日不变性,不是跨午夜回归的替身;跨午夜回归由 `test_real_cross_midnight_all_candidates_match_independent_dated_calculation` 负责,那条必须仍然在回退后转红。
- 文件内不再出现任何写死的分数哈希字面量。
### F2 · bridge 重复收集
当前 `tests/test_rectification_cross_midnight_gate.py` 的 re-export 让同一条用例在快速门里被收集两次(实测 4 failed = 2 × 2)。确认这是刻意还是副作用:
- 若刻意(只为让 glob 命中),在文件顶部注释写明「用例会被重复收集,计数非独立用例数」——执行方进度记录里已有「桥接重复收集的 14 项,不能算 14 项独立用例」的说法,把它落到代码注释里。
- 若非刻意,改为不产生重复计数的挂载方式。
验收标准:快速门里该组的用例数与直接跑原文件一致,或有书面理由写在文件内。
### F3 · 查证 `block_scan` 重算是否经过被修 helper(决定 BUG-984 优先级)
这是 Claude review 的遗留问题,成本低但改变 BUG-984 的定级:
- `frontend/src/lib/rectification-agentic/v9/score-persist.ts``block_scan` 分支只比 `evidenceLedgerFingerprint` 即返回 `cached:true`,不读 `readV9EngineScoringIdentity()``minute` 分支有 `cachedEngineScoreIsReusable` 身份门。
- `late_night``23:0003:59`299 分钟)与 `unknown`1439 分钟)都超过 `MINUTE_GRID_MAX_SPAN_MINUTES = 120`,因此都走 `block_scan`——**而这两类窗口恰恰是最容易跨午夜的**。
要查证的是:`block_scan` 的真实重算路径是否会调用到 `merge_transition_proximity()`
- **若会**BUG-981 在 `late_night` / `unknown` 路径上等于没上线,BUG-984 升级为 BUG-981 的阻塞项,必须在宣称跨午夜问题已修之前解决。
- **若不会**:BUG-984 与本次修复解耦,可按原优先级排。
验收标准:给出调用链证据(从 `block_scan` 分支一路到 `merge_transition_proximity`,或证明不可达),结论写进 `BUG-984` 正文与其补单的 §0。**不得以「大概会」结案。**
### F4 · 记录
- `docs/BUG_HISTORY.md` 新增 `BUG-985`(门禁级浮点哈希断言),关联 `BUG-981`;在「防复发」里写明:**同日/不变性类回归一律用同机相对比对,不得写死跨机浮点字面量**,并引用 `test_rectification_engine_memoization.py` docstring 里已有的同类教训。
- `BUG-981` 正文补一行:门禁级测试问题已单独立号 `BUG-985`,核心修复本身经独立 review 通过。
- `BUG-984` 正文补 F3 的结论。
- `docs/tasks/README.md` 状态板加一行;实现合入 staging 的同一次推送里改状态。
- `CHANGELOG.md` 不写(纯测试改动,无用户可感知行为变化)。
## 6. 让步顺序
1. **最先保 F1** —— 它是 `aa46da10` 的合入阻塞项,不修则整条跨午夜修复上不了 staging。
2. 其次 F3 —— 只是查证,成本低,但它决定「跨午夜问题是否真的修好了」这句话能不能说。
3. F2 可延后,但延后必须在进度记录里写明快速门中该组存在重复计数的事实,不得让后来人把 18 当成 18 条独立用例。
4. **不得为赶工砍掉 F1 验收里「两台机器各跑一次」那条。** 只在一台机器上绿,正是这次出问题的原因。
## 7. 开工前置命令
```bash
git fetch origin --prune
git worktree add -b codex/rectification-cross-midnight-gate-fix-20260920 \
.worktrees/rectification-cross-midnight-gate-fix-20260920 \
origin/codex/rectification-cross-midnight-20260920
cd .worktrees/rectification-cross-midnight-gate-fix-20260920
git log --oneline -1 # 必须是 aa46da10 或其后代
python3 scripts/pre_work_check.py --remote-timeout 8 --command-timeout 45
```
复现命令(应在与执行方不同的机器上看到红):
```bash
python3 -m pytest tests/test_dasha_transition_proximity_cross_midnight.py -q
python3 -m pytest tests/test_rectification_*.py -q # 快速门同 glob
```
环境备忘:Claude 的 review 环境为 Linux + 系统 `python3` 3.13(可 `import swisseph`),无项目 `.venv`frontend **无 `node_modules`**,前端侧检查需在有依赖的环境做。执行方环境为 Windows + Python 3.11.7。两者浮点行为不同,正是本单的起因。
## 8. BUG 编号起点
`docs/BUG_HISTORY.md``origin/staging``f8e40e5d`)上的最大号为 **980**;分支 `aa46da10` 已占用 **981984**(均为 `investigating`,尚未合入 staging)。本单从 **BUG-985** 起。开工时对 `staging` 与本分支两边取最大号再顺延,并在进度记录里写明取的是哪一侧。
@@ -0,0 +1,30 @@
# 普通对话寒暄快速通道 · 待执行验收
状态:本地实现已独立审查,审查发现已修复并复验;Linux 完整前端、标准 DB、构建与 Static/gzip 验收通过。用户已授权提交推 staging,门禁/部署与真人验收待核验,交付状态以本轮进度记录为准。只用产品负责人提供的受控测试账号,不借用凭据、不记录用户原文或出生资料。
## 环境门
- [x] Linux 隔离环境 tsc 0 errorlint 0 error/120 既有 warnings;最终基线全量 3530/3530、候选 3566/3566,标准 DB 39/39 → 40/40。Git archive 保留链接、补容器 python3-yaml/rsync;未删除或削弱断言。
- [x] 基线 `fcad06372` 与候选在独立 Linux named volume 分别完整 build,均退出 0、`/` 为 Static。首页 HTML 去重引用 JS、逐文件 gzip level 9621,299 → 621,605 B+0.0493%,通过 ±2%。Windows EPERM 历史保留,不再代表该平台补验未完成。
- [ ] 审查后由获授权主会话提交/推 staging;核对 Gitea 门禁、迁移日志与 `/api/health` 部署 SHA。执行代理未做这些动作。
## 三种模式真人走查
本命、申报时段、无出生分钟各执行一遍。
- [ ] 普通输入「你好」「在吗」「谢谢」「晚安」「哈哈」「我回来了」,检查一句白话、≤20 字、无句号结尾、无客服套话/星月比喻/星盘主张。
- [ ] 从点击发送到完成不出现虚构思考、活动或技法面板;刷新和切会话再回来仍只有短回复,不冒出「已完成 N 步」。
- [ ] 记录发送前后点数:预留原额退回,净余额相同;服务端一条 reserve 对一条 refund,不能重复退款。不同模型价格不应假定为 1 点。
- [ ] 有效订阅用户:无点数退款凭空加点,免费轮不占已完成咨询 quota;定向自动化只覆盖无点数来源结算分支,真实订阅授权链仍需此项。
- [ ] 零点且无订阅账号仍被现有前端/服务端预留守卫拦住,这是本轮保留的边界,不宣称所有用户可无限免费聊天。
- [ ] 同会话继续发「你好,帮我看看事业」「?」「你在说什么鬼」「今年怎么样」,应按咨询处理;确认每轮排盘合同与真实咨询扣点未改变。
- [ ] 今日星语、主题引导、生时校正三个特殊入口不进分类。旧 birth_time_rectification API 入口仍按现有规则拒绝,不能误认为快速通道放行。
- [ ] 保存 RPC 失败时不得显示成功回复/完成;恢复后可重试。取消先赢时免费完成不得写入;完成先赢时取消不得再退款。
- [ ] 网络断连或关闭页面后通过历史恢复服务端已完成短回复,净余额不变。
## 模型与观测
- [ ] 受控调用统计确认寒暄只有一次所选模型调用,无工具/Skill/标题/摘要生成;客户端历史不能替换服务端最后完整一对可见历史。
- [ ] 记录分类 P50/P95、超时率和真实混合意图误判,不能把假模型单测当语义准确率。P50 >1.5 秒时按任务书评估降为 1.5 秒上限,本轮没有真实延迟数据,不擅降。
- [ ] 超时/坏 JSON/无效 schema/供应商错误均进入原咨询链。分类 tokens、配置版本与估算成本可观测;超时供应商未返回 usage 时标 unknown,不当真实零成本。
- [ ] 抽检 usage ledger(免费成功的真实 tokens/cost + metadata)与封闭 observability;不写用户问题、姓名、出生资料、密钥或模型原文到日志。