# 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。正式领取时再次核对远端;若被占用顺延,不覆写其它会话记录。