Compare commits

...
6 changed files with 199 additions and 0 deletions
+8
View File
@@ -848,3 +848,11 @@
- ~~现行采纳函数和历史表约束拒绝非零秒,采纳函数也不接受 v2 provenance。需明确迁移归属,并确认短区间的保护距离。~~
- ~~已复现短区间返回段外整分钟。Docker Desktop 返回 HTTP 500,当前证据为迁移原文和引擎复现。见 [执行进度](docs/tasks/PROGRESS-chart-choice-rounds-engine-20261113.md)。~~
- 已解除。按修订任务书完成秒级采纳并用隔离 PostgreSQL 读回。Docker Desktop 的完整数据库套件仍受 HTTP 500 影响,见进度。
## #279 · 旧窄范围停用标记和来源
- 历史表仅有既有采用、复用和查询 RPC,缺少附加永久审计标记的授权接口。需批准数据库侧方案,或允许本轮派生停用标记、另立持久化审计单。
- 部分已复用 v1 的档案规则不足以区分手选与按事件采纳。替代证据是原历史记录的规则与证据来源。需确认无法追溯的记录与早期 `segment-v1` 的处理口径。
- 实际默认试算为 ±15 分钟,任务书 §六描述 ±30 且限定组件改动。需确认是否同步更新服务端默认,或按实际范围显示试算文案。
- 快照数量需受控的 staging 数据源。隔离数据库可验证行为和原记录保留,不能据此推断真实受影响数量。
- 详见 [#279 进度](docs/tasks/PROGRESS-rect-p0-20261115.md)。
+11
View File
@@ -25513,3 +25513,14 @@
- 验证与防复发:公开双盘使用默认窗口,合成用例分别覆盖甲方 D1、乙方 D9、月亮四分边界、两人稳定、自报误差、纠正记录复用及过时记录拒用。详见 [#276 进度](tasks/PROGRESS-synastry-precision-20261114.md)。
- 关联记录:#224、#275、#276、BUG-1654、BUG-1679、BUG-1869。
- 修复版本:`codex/synastry-precision-20261114`,待独立验收。
## BUG-1890 | 旧答题窄范围仍可进入读盘
- 状态:blocked(旧记录停用范围和审计写入方式待拍板)。
- 首次发现 / 最近更新:2026-11-15 / 2026-11-15。
- 现象与触发:FIX-7 撤下比较按钮后,兼容接口仍可接收采用答题范围的请求。已有旧窄范围的档案仍可供读盘。
- 根因:采用 API 接收 `useAnswerWindow`。持久化旧记录的 `rectified_range` 是读时投影,不能单凭名称区分手动选边和证据推断。历史表未提供停用标记写入接口。
- 修复进度:本地关闭应用层答题窄范围写入,保留旧记录及历史读取。停用标记与下游回退待拍板。
- 验证与防复发:定向测试覆盖旧采用开关发出时不调用引擎、保存默认原范围、在隔离数据库读取原有记录。详见 [#279 进度](tasks/PROGRESS-rect-p0-20261115.md)。
- 关联记录:#279、#260、#272、#229、BUG-1886。
- 修复版本:任务分支 `codex/rect-p0-20261115` 本地草稿。
+5
View File
@@ -681,3 +681,8 @@ Prevention: 不要把 `lagna_seventh_strength` 或 `niryaana_shoola_strength`
多个会话在同一台机器上各开整份工作树(每份带 `frontend/.next` 构建产物),根分区 inode 到 100%(剩约 5,700),新工作树建不出来,`next build` 中途还会再吃掉几千个。`df -h` 看着有空间,`df -i` 才看得到。清理已推送、工作区干净的临时工作树(`git worktree remove`,分支保留)和 `.next` 后回到 65%。
Prevention: 开工作树前先看 `df -i /`;工作树用完、内容已推送就删掉;构建验证后删 `.next`(含 `standalone`);`node_modules` 一律软链到已有工作树,不在新工作树里装依赖。关联本地前端构建验证步骤(next build --webpack + next start)。
## 2026-11-15 · #279 Windows 预检
- Python 3.11、Gitea 远端和引擎适配器诊断通过。定向预检的 `test_preflight_fragment_scan_reports_authority_layers_and_risk_buckets` 依赖工作区里的 `.workbuddy` 镜像,独立工作树不满足该断言。
- 复验包按冻结提交建立干净工作树,锁定 Python 依赖。原归档校验与 smoke 行对比通过。见 [#279 进度](../tasks/PROGRESS-rect-p0-20261115.md)。
+25
View File
@@ -86,3 +86,28 @@
- active_rectification_selector.py 只接受 A/B/C/D 才计作已答和领域频次,并透传模板来源及未验证时段状态。网站使用实际题目允许的 choice、独立验证冲突;本轮回放仅提交网站接受的原生答案。
下一轮:重新按人物划分开发、验证、最终留出,最终留出另收新人物;按原始记录分辨率保留标签区间;固定 profile、代码哈希和只读事实的模拟器;偏差配对之外增加整点舍入和时辰不明;交完整轨迹,固定消融次序,报告覆盖—宽度和伤害,另测用途稳定性。这些是下一轮方案,本次未使用新人物或做用途稳定性试验。
## #279 引擎复跑补包
取源提交为 `845854ff79ac72c931daa4e34f97f7084e8f35d4`。从 `results/SHA256SUMS` 筛选 `scripts/` 下的 60 个 Python 模块。用上述提交的 Git blob 提取,逐项核对 SHA256,并保留路径。另核对清单里的 3 个 `.se1` 文件。提交中的相同星历路径为 `references/open_source_sources/vedic-astro-skills/antigravity/skills/vedic-calculator/scripts/ephe/`。当前 staging 在年运、咨询和合盘模块上已有其他提交,复验不得把这些版本混入。
在干净检出根目录解压源码补包和原 #272 归档,保留 `scripts/`、`references/real_case_calibration/` 和星历原目录。用 Python 3.11。需要显式设置 `PYTHONPATH` 时加入检出根和 `scripts/`,Windows 用分号,POSIX 用冒号。先执行:
python references/real_case_calibration/rect_repro_20261114/reproduce.py --output rect-smoke --workers 1 --smoke
烟测通过后重跑全量:
python references/real_case_calibration/rect_repro_20261114/reproduce.py --output rect-full --workers 8
核对原归档及其散列:
python references/real_case_calibration/rect_repro_20261114/verify.py references/real_case_calibration/rect_repro_20261114/results
烟测的每个解压 JSONL 与相应归档开头 8 行比较。验收证据:Windows 干净工作树上 32 行全匹配,原归档 2464 行全部通过校验。
| 依赖 | 许可证与获取位置 |
| --- | --- |
| Jyotisha 复跑模块 | MIT。源码补包保留源提交的 LICENSE |
| `pyswisseph==2.10.3.2` | AGPL-3.0。见 [PyPI 版本](https://pypi.org/project/pyswisseph/2.10.3.2/) 和 [作者仓库](https://github.com/astrorigin/pyswisseph) |
| `tzdata==2026.2` | Python 包 Apache-2.0。见 [Python tzdata 许可证](https://github.com/python/tzdata/blob/master/LICENSE) |
| `seas_18.se1`、`semo_18.se1`、`sepl_18.se1` | Astrodienst 的 AGPL 或专业许可。补包提供原路径与散列。按 [Swiss Ephemeris 官方下载及许可说明](https://www.astro.com/swisseph/swephinfo_e.htm) 获取并核对冻结散列 |
+37
View File
@@ -0,0 +1,37 @@
# #279 · 生时纠正 P0(2026-11-15)
- 执行者:Codex。任务书由 Claude 在评论 44523 确认。
- 代码基线:`origin/staging` 的 `405876ad2e5a6312145d9061e5c738bd5dc506ac`。从该提交建立独立工作树,再快进到任务分支的任务书提交 `a6611180e863db1cf9a27e4ba90818dc3e50cb25`。
- 关联 BUG-1886:FIX-7 已撤下页面比较按钮。复核发现采用 API 和历史复用 RPC 仍能写入旧窄范围,报告仅降级把握,仍读取候选段。
- 上线范围按 Git 引用核对。下表日期为代码进入所核对的 staging 历史的日期,实际部署时间待负责人核对;生产控制分支 `7b620c7a2` 无这些 11 月提交,未据此推断线上实际镜像。
| 来源 | 可验证的写入与版本 | 下游与快照 |
| --- | --- | --- |
| 问卷答题段 `user_choice_from_answers` | 11-14,`chart_choice_adopt_v4` 写原始历史与档案采纳来源。页面 FIX-7 撤掉入口,直接请求仍可写。 | 长报告和合盘从档案取采纳段。年运或聊天若读取采纳时刻和状态也会受影响;账户、星盘和生时纠正首页会取采纳时间。已有长报告、年运正文和导出使用已保存的报告数据。 |
| 11-06 旧选盘采纳 | `chart-choice-v1` 共用 `source=user_choice`;11 月事件证据链可留下 `rule=evidence`,但只在完整历史决策中有 `decided_by`;证据也可能来自外貌或补充问答。复用 v1 记录会重写档案副本的 `rule=reused_record`,原历史记录保留原值。11-13 报告读取才把采纳范围转换为 `rectified_range`。 | 生时纠正首页与账户使用采纳时间;长报告用 `active_birth_provenance` 及选边重建区间。这些新输出可以回退,已生成快照和导出须保持原样。 |
| #229 的显式分盘选边 | 11-13 多轮数据为 `chart-choice-v2`、`rounds` 与 `segment`。部分被续问的老记录仍是 v1;原规则 `user_override` 可证明显式反选。 | 继续作为历史和当前分盘依据。不得仅凭 `rectified_range`、`user_choice` 或本地日期整批停用。 |
| 早期事件校正段 | 旧流程另有 `segment-v1` 与 `read_report_candidate_range`。前者存校正采纳,后者可读候选区间,不能仅凭候选区间认定已采纳。 | 旧咨询从独立 RPC 取候选范围。需先决定是否纳入本次回退,避免改变明确采纳或数据不足的旧记录。 |
| 原始填报 / 范围中点 | `reported_time_default`、`window_midpoint_default` 与原始声明。 | 是回退目标。未填范围的试算窗口单独说明,不能作为用户已确认的误差。 |
仍需负责人确认的范围和存储方案见下方记录。已有快照受影响数量 blocked:本轮只使用隔离测试环境,没有获准查询含用户记录的数据库。
## 待拍板
- **停用标记的持久化。** `chart_choice_records` 已撤回应用角色的表权限。现有 RPC 只允许老记录复用、新记录采用和只读查询,没有附加标记的入口。自己的档案和代管人物权限不同,不能使用应用代码绕过历史记录表权限。建议由数据库负责人确认走独立审计记录,或批准在原历史 JSON 新增只写一次的标记及受控回填。若本单必须保持零迁移,可先按读取规则派生「已停用」及原因,同时将持久化审计另立单。
- **已复用的旧 v1。** 档案的规则被复用覆盖时,可以在对应历史还在可读窗口内时找回原规则。历史缺失或规则无法区分手选与证据时,建议保留明示手选和来源不明的记录,先只停用有明确证据的旧窄范围。待确认早期 `segment-v1` 是否属于本轮。
- **生时纠正实际默认值。** 任务书 §六以 ±30 为默认,并限定只改组件。现有 `DEFAULT_RANGE_MINUTES` 是 ±15,服务端也复用该值。建议允许同步改服务端默认;若只改本页文案,按实际区间说明试算,用户手填范围保持原意。
## 已做与验证
- 本地改动拦截 `POST /api/chart-choice/adopt` 的 `useAnswerWindow`,内部保存也拒绝旧开关,避免新的问卷采用写入窄范围。历史枚举、表和 RPC 未改。
- 定向选盘页面/服务 7 项通过。隔离 PostgreSQL 的历史记录测试 2 项通过。
- 预检的 Python、Gitea 远端、碎片扫描和引擎适配器通过。定向预检一项未过,独立工作树缺少扫描测试期望的 `.workbuddy` 镜像目录。
- 在干净工作树按冻结提交 `845854ff79ac72c931daa4e34f97f7084e8f35d4` 安装锁定依赖。归档 `verify.py` 核对 2464 行通过。四组 smoke 各 8 行解压后与归档逐行一致。
- 本地源码补包已核对 60 个 Python 模块和 3 份星历的冻结散列。包提供 Python 源码和星历原路径、散列及授权获取说明。复跑说明见 `docs/research/rect-repro-20261114.md`。
| 既有测试原值 | 新值 | 原因 |
| --- | --- | --- |
| `useAnswerWindow=true` 通过应用服务写入旧窄范围 | 服务在调用引擎前拒绝,保留之前已写入的原始范围 | 产品要求关闭新写入 |
| 通过应用服务构造数据库中的旧记录 | 用现有旧版本 RPC 模拟数据库中已存在的旧记录,保留其来源、秒级窗口和复用断言 | 继续验证原记录的读取与兼容性 |
待处理:旧档案的完整回退及审计标记、各下游合同与快照统计、四条模拟器反例、页面试算文案与 390 像素验收。当前进度不作为完整修复验收。
+113
View File
@@ -0,0 +1,113 @@
# 生时纠正 P0:旧窄范围退出报告、弃权语义、模拟器隔离、引擎复跑包(2026-11-15,Claude)
Bug 编号:BUG-1890。
## 背景
占星师 11-15 复核了 #272 的复验包,主要统计独立复算一致。她排了下一包的验收顺序:
1. **P0:**
- 证明「应用」已禁用;
- 旧的窄范围已撤回;
- 弃权语义正确;
- 原始记录保留。
2. 补齐源码依赖,让她能复跑引擎;做模拟器隔离的反例测试。
3. 日期区间和用途稳定性。**本任务不做。**
产品负责人 11-15 定了两件事:
- **旧窄范围:** 报告一律改用原范围。旧记录留在库里备查,但报告、聊天、导出和首页都按用户填的时间和原范围读,相当于自动改回,并留下审计痕迹。
- **源码:** 给占星师的复跑包只放复跑需要的 Python 模块、星历和目录重建说明。不放前端、部署配置或密钥。
## 一、盘点(先做,结果写进 PROGRESS)
列出所有会让「读盘窗口」比用户原始范围窄的路径,每条写清楚以下几项:
- 来源字段,例如 `user_choice_from_answers`、11-06 按事件采纳的 `rectified_range`、#229 分盘换座线两侧的选边,以及其他来源;
- 是在 staging 还是生产上线,什么时候上线的;
- 会进哪些下游:长报告、年运、聊天、星盘页、导出、首页;
- 已生成的报告快照会不会继续沿用旧窗口。
只盘点,不顺手改。
## 二、旧窄范围退出下游
1. **适用范围:** 来源属于下面两类的记录。
- 答题或事件得出的范围:`user_choice_from_answers`、11-06 按事件采纳的 `rectified_range`,以及盘点中发现的同类来源。
- 处理方式:下游一律按用户原始填写时间加原始范围读。没有填时间的,按原范围中点读。
2. **#229 的分盘选边**(用户按「哪种更像你」选了换座线某一侧)**这次不改。** 在盘点里单独列出,交产品负责人和占星师另定。
3. **记录不删不改:**
- 原记录保留原值和来源;
- 新增一个停用标记,写明原因,例如「BUG-1890:答题缩窄未验证」;
- 读取层看到这个标记,就退回原范围。
- 首页显示填写的时间;历史里能查到旧记录,标「已停用」。
4. **已生成的报告快照:**
- 不自动重写;
- 用户下次重新生成时按原范围;
- 在 PROGRESS 里统计受影响的快照数(staging 本地库能统计就统计,统计不了写 blocked)。
5. **前端和 API 不能再新写窄范围:**
- `/api/chart-choice/adopt` 收到 `useAnswerWindow` 时直接拒绝(4xx,带明确说明);
- 迁移不改,旧枚举值保留,旧记录还要能读。
## 三、弃权语义
占星师指出:555 次「弃权」(停在「没有可以再区分的题」)里,有 52 次在停下之前已经把真实时间排除了。
1. **回放轨迹拆成四个字段:**
- `stop_reason`;
- `set_changed`:候选集合是否比原窗口小;
- `reverted_to_original`:最终是否退回原集合;
- `apply_allowed`:是否允许应用(现在恒为 false)。
2. **网站行为:** 任何停止原因下(包括弃权),保存的都是填写时间加原窗口,不能悄悄用受损的区间。
- 写一个反例测试:先缩窄、把参照时间排除在外,再停在「没有可以再区分的题」,断言保存的仍是 `reported_time_default` 和原窗口。
## 四、模拟器隔离反例(`references/real_case_calibration/rect_repro_20261114/` 的测试)
1. 把评分用的真实时间换成另一个值,事件答案不能变。
2. 题目选项重新排序后,选中的事实语义不能变。
3. 胎记题传空数组时,记作「未知」,不能当成「确定没有」。
4. 同一条人生事件衍生出的毕业、就业、搬迁,记录共同的事实链 ID,汇总时不算三条独立经历。只加标注和统计,不改评分。
## 五、给占星师的引擎复跑包
1. 按 `results/SHA256SUMS` 里列出、但 ZIP 里没有的 63 项打包:
- `scripts/`、`scripts/chart_choice`、`scripts/chart_dasha`、`scripts/rectification`、`scripts/report_render` 中回放实际加载的模块;
- 3 份星历文件。
2. **原目录重建说明:**
- 解压到哪里、`PYTHONPATH` 怎么设;
- 一条命令跑 `--smoke`,再跑全量。
3. **许可清单:** 每个依赖和星历文件的许可证名称;不允许再分发的写明,改为给下载地址。
4. **版本:**
- 以 845854ff 为准;
- 如果复跑用的 scripts 和当前 staging 不同,写明差异;
- 不能拿资料仓里的同名文件顶替。
5. **在干净目录里自测一次:** `--smoke` 逐行一致,`verify.py` 通过,并写清楚需要 Python 3.11。
## 验收
- **测试:**
- 旧 `user_choice_from_answers` 和 11-06 `rectified_range` 记录:长报告、年运、聊天、导出、首页都按原范围读,不出现 `minute_confirmed`;
- 停用标记写入、可审计;
- `useAnswerWindow` 被拒绝;
- 弃权反例;
- 四个模拟器反例。
- **证据:**
- 新用户和历史用户各一组,覆盖 API 响应、存储前后、页面截图(390 宽)和导出文件;
- 本地能做到的先做,staging 部署后补;
- 用合成账号和资料,不碰真实用户。
- **门禁:** Python quick gate、chart-choice 和报告相关前端测试、`tsc`、`npx eslint .` 0 errors。
- **文档:**
- BUG_HISTORY 新增 BUG-1890,只在文件末尾追加这一节;
- 写 PROGRESS;
- 复跑包说明写进 `docs/research/rect-repro-20261114.md` 的新一节。
## 六、补充(11-15):默认 ±30 窗口的说法
产品负责人 11-15 定了一条规则:用户没填范围时,默认的 ±30 分钟只算「研究情景」,个人报告、年运、合盘、生时纠正都适用。报告侧由另一个会话改(BUG-1891 起)。生时纠正页面的文案在本任务里改:
1. **用户没填范围、用的是默认 ±30 时:**
- 星盘行不写「已定」,改写「前后 30 分钟试算里没有变化」;
- 不写「真实时间在这段里任何一处,下面六张盘都一样」;
- 不出现「稳定」。
2. **用户自己填了范围时:** 「已定」仍然只表示「在你填的范围里没有换座」。文案要把这层意思说出来,不暗示出生时间已确认。具体写法在 PROGRESS 里给出,等我验收时确认。
3. **只改 `frontend/src/components/chart-choice/` 和对应测试。** 不碰 `scripts/report_render/birth_precision_window.py`,也不碰 `birth_window_label_zh`,那两处归另一个会话的任务。