diff --git a/docs/tasks/README.md b/docs/tasks/README.md index 9bb37f49..ea96b527 100644 --- a/docs/tasks/README.md +++ b/docs/tasks/README.md @@ -136,7 +136,8 @@ | — | `PROGRESS-report-progress-20260909.md` | 生成等待屏只有 spinner 与秒表:后端 `progressPercent` / `progressPhase` 与分章行已产出,前端解析后一字未渲染,且分章行在 `generating` 时根本不出服务端。改为按章分格进度条 + 章节清单,停滞 90 秒改「用时较长,仍在写」;不画百分比条、不做插值动画、不报预计剩余 | 已验收 | `848e39e6`、`5565b632`(BUG-601) | | `TASK-report-chart-render-20260909.md` | `PROGRESS-report-chart-render-20260909.md` | 报告页所有分盘标题下空白:引擎 22 张 `` 被 `skipHtml` 丢弃(09-06 Markdown 直渲回归)。引擎旁加 ```jyotish-chart 围栏 JSON,前端 zod 校验后用自绘北印式组件全部渲染;不放开原始 HTML | 已验收(2fdcb14f):tsc/lint 0 error、chart 测试 29/29、`/` Static、gzip +0.96%;围栏构造在 try 外的 P2 并入 `TASK-upstream-sync2-fix-20260909.md` 任务 3 | `codex/report-chart-render-20260909`(BUG-607) | | `TASK-report-chart-layout-fix-20260909.md` | `PROGRESS-report-chart-layout-fix-20260909.md` | 星盘渲染回归修复:22 张北印盘全部重叠(浮动 + 负 margin 凑两栏对连续多对无效)+ 滚动时 `components` 每渲染新建导致整篇报告卸载重建、页面卡;改 rehype 成组进既有两栏 grid、文章树 `useMemo`、目录状态下沉 | 待验收 | `87daffe2`(BUG-616/617);已 merge origin/staging `df090fd5` | -| `TASK-report-provenance-binding-20260915.md` | `PROGRESS-report-provenance-binding-20260915.md` | 专业报告溯源绑定:`result_binding` 只绑到中间态包(实测 18/36 个顶层键、6.24 MB / 交付 10.04 MB),`full_report_pack`、`chart_identity`、`timing_precision_contract` 等全在覆盖外,而质量门只比自洽所以恒 passed(BUG-693);被哈希的载荷含墙钟 `ai_and_audit.summary.elapsed_seconds`,同一输入两次跑出不同 `result_hash`(BUG-694)。末尾按交付对象重绑 + 显式排除集写进回执 + 质量门补覆盖校验;上游 09-14 的有界投影实测收益 < 0.1 秒,不抄 | 待验收 | `codex/report-provenance-binding-20260915` | +| `TASK-report-provenance-binding-20260915.md` | `PROGRESS-report-provenance-binding-20260915.md` | 专业报告溯源绑定:`result_binding` 只绑到中间态包(实测 18/36 个顶层键、6.24 MB / 交付 10.04 MB),`full_report_pack`、`chart_identity`、`timing_precision_contract` 等全在覆盖外,而质量门只比自洽所以恒 passed(BUG-693);被哈希的载荷含墙钟 `ai_and_audit.summary.elapsed_seconds`,同一输入两次跑出不同 `result_hash`(BUG-694)。末尾按交付对象重绑 + 显式排除集写进回执 + 质量门补覆盖校验;上游 09-14 的有界投影实测收益 < 0.1 秒,不抄 | 已验收(`1d2aeffa`):三次 CLI + 三个 `PYTHONHASHSEED` 哈希全同、变异法验证覆盖 33/40 键、耗时中位数 2737 ms(修复前 2769 ms)、`npm test` fail 27 = 基线;快速门唯一红的 `test_shadbala_endpoint_returns_ranked_planet_strength` 在修复前同样红(timezone 依赖缺)。遗留补漏单 `TASK-report-binding-scope-lock-20260915.md` | `1d2aeffa` | +| `TASK-report-binding-scope-lock-20260915.md` | `PROGRESS-report-binding-scope-lock-20260915.md` | 验收补漏单:BUG-694 的「排除集只能三类」是纸面规矩——质量门与测试引用的是生产者同一对常量,往常量里加第四类会自动放行;把允许集的字面量钉进合同测试(改动即红)。另把 `--profile quick` 必红的 `timezone inference dependency unavailable` 记进 `BLOCKED.md`(修复前同样红,非回归)。不新增 BUG 编号,只更新 BUG-694 | 待领取 | — | ### 前端基础与工程 diff --git a/docs/tasks/TASK-report-binding-scope-lock-20260915.md b/docs/tasks/TASK-report-binding-scope-lock-20260915.md new file mode 100644 index 00000000..c47dfdcb --- /dev/null +++ b/docs/tasks/TASK-report-binding-scope-lock-20260915.md @@ -0,0 +1,103 @@ +# TASK · 补漏单:把绑定排除集钉成字面量 + 记录 timezone 环境缺口 — 2026-09-15 + +- 基线:`origin/staging` @ `1d2aeffa`(BUG-693/694 实现已合入并**已由我验收通过**)。 +- 分支:`codex/report-binding-scope-lock-20260915`,worktree `.worktrees/report-binding-scope-lock-20260915`。 +- 关联:`TASK-report-provenance-binding-20260915.md`(本单是它的验收补漏)、BUG-694(本单补它的防复发条)。 +- 串行:只加一个合同测试 + 改 `BLOCKED.md`。**不改任何生产代码行为**,与在途分支无重叠。 + +## 1. 缘起(验收实证) + +BUG-693/694 的实现验收通过:三次 CLI 加三个 `PYTHONHASHSEED` 哈希全同(`784cdda5…`),变异法独立验证覆盖 33/40 顶层键,耗时中位数 2737 ms(修复前 2769 ms)。本单不推翻其中任何一条。 + +验收时发现两个**没有被任何机制挡住**的洞: + +### 1.1 排除集的"只能三类"目前是纸面规矩 + +BUG-694 的防复发条写的是「排除集只能是自引用 / 生成时刻 / 墙钟三类……加第四类要改任务书」。实际约束链是: + +- 生产者:`scripts/calculation_profile_contract.py:81` / `:86` 定义 `ALLOWED_BINDING_EXCLUDED_TOP_KEYS` / `ALLOWED_BINDING_EXCLUDED_PATHS`,`:458` / `:459` 把它们原样写进回执。 +- 质量门:`scripts/full_report_quality_gate.py:14-21` **import 同一对常量**,`:197` / `:200` 拿回执里的排除集减去这对常量判「意外排除」。 +- 测试:`tests/test_calculation_profile_contract.py:585-586` 断言 `set(scope[...]) == set(ALLOWED_...)`。 + +三处引用的是同一个常量,所以**谁往常量里再加一条,门自动放行、测试自动变绿**。这条防复发现在只能挡住外来或手工篡改的包,挡不住它真正想挡的那件事(排除集悄悄变宽)。 + +本轮那次扩集就是例证:D2 原文只批了 `ai_and_audit.summary.elapsed_seconds` 一条墙钟路径,落地扩成了 `**.elapsed_seconds` 通配并新增 `**.called_at` / `**.cache_created_at` / `**.cache_expires_at`。理由(跨进程可复现)成立、也写进了 BUG-694,但任务书没有被改,也没有任何东西要求改。 + +### 1.2 `--profile quick` 在本机必红一条,没人记 + +``` +FAILED tests/test_api_server_security.py::test_shadbala_endpoint_returns_ranked_planet_strength +WARNING [api_server] shadbala advanced layer failed: timezone inference dependency unavailable +E assert ['core_sixfold'] == ['core_sixfold', 'advanced_evidence'] +``` + +`739 passed, 1 failed`。同一条在修复前的 `039b0a26` 上**同样红**,与 BUG-693/694 无关。`BLOCKED.md` 里没有这条;实现方进度记录里写的是自己机器缺 `mcp`,也没进 `BLOCKED.md`。结果是下一个人跑快速门会重新查一遍同一个环境问题。 + +## 2. 决策记录 + +| 决策 | 内容 | +| --- | --- | +| D1 | **不动现有排除集的内容。** 四条墙钟/时间戳路径按 BUG-694 的记录保留,本单不重新开会讨论它们该不该在里面。 | +| D2 | **把当前允许集的字面量钉进合同测试**:常量被改动(增、删、改名)即测试红,由人决定放不放行,而不是自动通过。这是本单唯一的行为约束。 | +| D3 | **不把这条做成运行时校验**(不在生产代码里再写死一份清单)——两份清单会漂移,且门在运行期要挡的是外来包,语义不同。锁在测试层。 | +| D4 | **`BLOCKED.md` 如实记录 timezone 缺口**,写明失败的测试名、warning 原文、以及"修复前同样红"这个对照证据。不得写成通过,也不得为了让门变绿去改那条断言。 | +| D5 | 不新增 BUG 编号。本单是 BUG-694 防复发条的补强,落在原记录上。 | + +## 3. 硬红线 + +1. **不改生产代码。** `scripts/calculation_profile_contract.py`、`scripts/full_report_quality_gate.py`、`scripts/jyotish_engine.py` 一行不动。若发现不改生产代码就锁不住,停下来写进度记录说明,不要自行改。 +2. 不得放宽或删除 BUG-693/694 的既有判据与断言。 +3. `tests/test_calculation_profile_contract.py` + `tests/test_full_report_quality_gate.py` 保持全绿;`--profile quick` 除 §1.2 那条 timezone 缺口外不得新增失败。 +4. 不改 `.gitea/workflows/**`,不提升 `main`。 + +## 4. 任务分解 + +### 任务 1 · 允许集字面量锁(P1) + +- 在 `tests/test_calculation_profile_contract.py` 新增一条合同测试,断言两个常量的**完整字面量与顺序**等于当前值: + - `ALLOWED_BINDING_EXCLUDED_TOP_KEYS == ("generated_at", "report_quality_gate", "shared_full_report_authority")` + - `ALLOWED_BINDING_EXCLUDED_PATHS == ("ai_and_audit.summary.elapsed_seconds", "**.elapsed_seconds", "**.called_at", "**.cache_created_at", "**.cache_expires_at")` +- 测试里写明三类归属与"改这里要先改任务书并在 BUG-694 补记"的注释,不要只留一行断言。 +- 验收标准:本地把常量任意加一条(例如 `**.generated_by`)→ 该测试红;恢复后绿。进度记录贴这两次的输出。 + +### 任务 2 · 环境缺口入账(P1) + +- `BLOCKED.md` 新增一节,按 D4 写清:失败测试名、warning 原文、`739 passed, 1 failed` 的数字、以及"在 `039b0a26`(修复前)上同样红"这条对照证据,并注明**这不是 BUG-693/694 的回归**。 +- 同时写明它对验收的影响:本机 `--profile quick` 的通过口径是"除这一条外全绿",谁要宣称快速门全绿,必须先补上 timezone 推断依赖。 +- 验收标准:条目里能直接复制出复现命令;不出现"通过"字样。 + +### 任务 3 · 记录(P2) + +- `docs/BUG_HISTORY.md` 的 **BUG-694**:更新「最近更新」日期,防复发条补一句——允许集有字面量合同测试守门,改动必须先过测试再改任务书。不新增编号、不改原有的现象/根因/修复正文。 +- `docs/tasks/PROGRESS-report-binding-scope-lock-20260915.md`:贴任务 1 的红/绿两次输出与 `--profile quick` 的失败清单。 +- `CHANGELOG.md` 不写(对用户不可感知)。 + +## 5. 不在本单范围(验收时记下的观察项,不要顺手改) + +- `report_quality_gate` / `shared_full_report_authority` 现在挂在 `sanitize_professional_report_reference` 之后,不再过脱敏。实测差异只有 `limitation_reference: null` 这类空值被保留,无路径或内部字段泄漏,前端不消费这两个键。**本单不动**;要改需单独一单并说明"交付对象最后必过一遍 sanitize"这条保证要不要恢复。 +- `cmd_yoga` 的 `list(set(...))` → `sorted(...)` 是 BUG-694 的第二根因修复,已记录在案,**不要回退**。 +- BUG-692 那条线:候选数 = 1 时会出现「还剩 … 里 1 个候选。按现有信息分不开。」自相矛盾(修复前说「这两分钟」,同样错,不是回归)。属于 `TASK-rectification-invite-copy-and-assertions-fix-20260915.md` 的后续,不在本单。 + +## 6. 让步顺序 + +1. 若字面量锁与将来某次合法扩集冲突:先改任务书/BUG 记录,再改测试字面量,顺序不得颠倒。 +2. 若 timezone 依赖能在本机装上(需要联网或系统包),**优先装上并让那条测试真绿**,`BLOCKED.md` 只保留"曾经缺、现在已补"的一行说明;装不上再按任务 2 入账。 +3. 任何情况下不得通过修改 `test_shadbala_endpoint_returns_ranked_planet_strength` 的断言来让门变绿。 + +## 7. 开工前置命令 + +```bash +git -C /workspace/Jyotisha status -sb | head -1 +git -C /workspace/Jyotisha fetch origin --prune +git -C /workspace/Jyotisha worktree add -b codex/report-binding-scope-lock-20260915 \ + .worktrees/report-binding-scope-lock-20260915 origin/staging + +cd .worktrees/report-binding-scope-lock-20260915 +/workspace/Jyotisha/.venv/bin/python -m pytest \ + tests/test_calculation_profile_contract.py tests/test_full_report_quality_gate.py +/workspace/Jyotisha/.venv/bin/python scripts/run_quality_gate.py --profile quick # 预期:739 passed, 1 failed(timezone) +``` + +## 8. BUG 编号起点 + +**本单不新增编号**(D5)。`docs/BUG_HISTORY.md` 当前最大号 BUG-694;只更新 BUG-694,不要占 695。若执行中发现真实回归,再按当时最大号 +1 新开。