Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
66 lines
4.6 KiB
Markdown
66 lines
4.6 KiB
Markdown
# TASK · 报告导出 CRLF 围栏合同冲突(2026-09-24)
|
||
|
||
## 状态 / 基线
|
||
|
||
**待领取。产品 2026-09-24 已批准 D5 有限例外(见决策记录 D14),本文件即执行授权。**
|
||
|
||
基线更新:`origin/staging` 含已部署的 `1420471a`(两单组合已合入),从该基线开分支 `codex/report-chapter-export-fix-20260924`。
|
||
|
||
基线 `5ff43b47f38e378918c8b6bd967228185d7f02fe`,发现于尚未提交的 `codex/report-chapter-export-20260924`,组合候选 `codex/chart-report-integration-20260924`。原报告单完成本地实现后,独立审查发现本项;继承旧剥离器缺陷,不是新增回归。后续必须在两单组合版本之后串行执行,不覆盖在途工作。
|
||
|
||
## 事故实证
|
||
|
||
- `frontend/src/lib/report-chart-block.ts` 的 `FENCE_RE`(当前83行)在语言标记后仅允许LF;`stripReportChartBlocks`(当前202–204行)直接使用该正则。
|
||
- 新 `report-export-blocks.ts` 的正文块与旧 `ordinaryReportDownloadMarkdown()` 都调用旧剥离器。
|
||
- 只将真实引擎虚构fixture `frontend/tests/fixtures/report-density-fictional-reader.json` 的markdown从LF转为CRLF,构造正文块并join:正文25块,下载中残留22个 `jyotish-chart` JSON围栏;仍与旧导出逐字节相等。分盘另能正确提取22个出现项。
|
||
- 相关105条测试通过未覆盖“含真实图盘围栏的CRLF输入无JSON”;已有CRLF字节等价测试不足以证明禁止JSON合同。
|
||
|
||
复现核心(在既有Node22/tsx环境运行,仅读虚构fixture,不写资料):
|
||
|
||
```ts
|
||
const markdown = fixture.markdown.replace(/\n/g, "\r\n");
|
||
const body = buildReportExportBlocks({ markdown }).filter(b => b.group === "body");
|
||
const output = joinExportBlocks(body);
|
||
console.log({
|
||
residualJsonFences: (output.match(/```jyotish-chart/g) ?? []).length,
|
||
equalsOldExport: output === ordinaryReportDownloadMarkdown(markdown),
|
||
});
|
||
// 实测:{ residualJsonFences: 22, equalsOldExport: true }
|
||
```
|
||
|
||
## 根因与旧防线
|
||
|
||
语言标记后 `\n` 不匹配 `\r\n`;投影允许供阅读组件消费的图盘结构,普通导出还需剥除围栏。BUG-999/1003投影及安全SVG防线不应撤销,当前缺的是下载剥离器换行边界;不能把ordinaryOutputLeaks为空替代无JSON独立断言。
|
||
|
||
## 决策记录
|
||
|
||
- 原单D5明确不可让步:正文全选与**旧版本**普通导出逐字节相同。
|
||
- 原单T1同时禁止JSON图盘围栏,CRLF输入下这两条无法同时成立。
|
||
- 用户后续D13只允许并要求SVG,不授权保留JSON,也未授权改变D5。
|
||
- **D14(产品 2026-09-24 批准)**:D5 对 CRLF / 混合换行输入中的 `jyotish-chart` JSON 围栏残留作有限例外——新导出必须删除这些围栏,因此 CRLF 输入下与旧导出不再逐字节相同;差异**只能**是被删掉的围栏(及其紧邻的围栏换行)。LF 输入仍须与冻结的旧导出逐字节相同,其它正文、安全 SVG、顺序一律不变。
|
||
- 仍禁止只比较两个共同修改后的 helper 来声称历史行为等价(见任务分解第 1、4 条的冻结 golden)。
|
||
|
||
## 硬红线
|
||
|
||
不标准化整篇换行,不改SVG几何/投影allowlist,不改生成/API/数据库,不新请求,不动依赖或workflow。保留单一大纲、既有下载器、全部测试名称与三栏断言记录。
|
||
|
||
## 任务分解(获准后)
|
||
|
||
1. 冻结基线LF golden旧导出结果及hash;另记录CRLF旧输出与22围栏事实。
|
||
2. 最小修改语言标记后的换行匹配为 `\r?\n`,检查边缘空白处理而非全篇replace。
|
||
3. 新增真实golden转换CRLF/混合换行测试:所有正文无JSON围栏;原安全SVG、其它文本字节与顺序保持;ordinaryOutputLeaks为空;分盘出现数不变。
|
||
4. LF golden必须与冻结旧版本结果逐字节相等。CRLF差异必须仅为获准移除的围栏,不得仅比较两个共用修改后helper的函数。
|
||
5. 跑tsc/lint/全量名称多重集合与失败诊断对照、实际下载;更新D5有限例外、PROGRESS、BUG-1020、BLOCKED、testing。构建/真机缺口如实保留。
|
||
|
||
## 让步顺序
|
||
|
||
不让步安全投影或JSON禁止,不静默改D5;无授权则保持blocked。构建环境缺口可留清单,不能写成通过。
|
||
|
||
## 开工前置命令
|
||
|
||
先 `git status -sb`,再 `git fetch origin --prune`,核对两单实际交付基线;获准后创建独立worktree,不在脏树切分支/reset/stash。重读AGENTS/frontend约束及本次BUG记录,Node22下跑报告定向基线。没有真实账号不得借凭据验收。
|
||
|
||
## BUG编号
|
||
|
||
组合候选核对最大号1019,本项暂记BUG-1020。正式领取时再次核对远端;若被占用顺延,不覆写其它会话记录。
|