摘要

  • RFC 5256 让排序和线程生成具有明确规则,但每个结果仍是特定检索范围、算法、排序比较与邮箱状态下的投影。
  • 线程边可以来自 Message-ID 引用、缺失祖先的占位节点或主题回退合并;这些机制都不能单独证明回复意图、完整血缘、保管过程或送达结果。

屏幕先讲出了故事

事故复盘时,六封邮件被画在同一根节点下。法务把它看成完整通知链,管理层把它看成指令与确认,工程团队把它看成问题已经逐级闭环。三方看到的像是历史,底层协议却只承诺了一个排列结果。

THREAD 不是先发现一个天然存在的“会话”,而是先执行搜索。日期范围、关键词、邮箱选择或状态不同,参与构树的邮件集合就会不同;原始邮件一字未改,树也可能换形。问题因此不是“这棵树是否漂亮”,而是“它回答了哪个查询、使用了什么算法、基于哪一刻的邮箱状态”。

这正是现实层与符号层需要分开的地方。邮件及服务器元数据属于记录层;检索、解码、归一化、排序和构树属于转换层;客户端的缩进、连线与“对话”标签属于展示层。展示层可以把复杂性压缩成一个眼神就能理解的图形,但不能因此继承记录层没有给出的权威。

一条命令里有多位控制者

客户端选择检索条件、字符集、排序条件或线程算法。服务器决定当前邮箱快照,解析头字段,执行规范要求的比较与冲突处理,最后返回序列号或 UID。发件软件生成了 Message-ID、References 和 In-Reply-To。界面又决定用户最后看见什么。

因此,每一层都可能“正确”,最终判断仍可能错误。服务器完全依照 REFERENCES 算法处理一条伪造引用,客户端也完全忠实地画出父子线,用户仍会把一条从未发生的回复关系当成事实。反过来,一个父节点缺失,可能是查询把它排除,也可能是邮箱从未收录,或者它已在当前快照前被删除。相同的空洞并不对应唯一历史。

ORDEREDSUBJECT 从不声称自己知道谁回复谁

RFC 5256 直接把 ORDEREDSUBJECT 称作“穷人的线程”。它按固定步骤提取基础主题,把基础主题相同的邮件归为一组,再按发送日期排列。每组第一封成为根,之后所有邮件都成为这个根的直接子节点;算法明确没有孙节点。

这不是一个失败的回复树,而是另一种产品:主题聚类。它在引用字段缺失时依然实用,也适合快速浏览邮件列表。但平面结构无法说明第二封究竟回复第一封还是第三封,也无法保证同题邮件属于同一讨论。

基础主题本身也是处理结果。编码词会被转成 UTF-8,制表符、续行和重复空格会折叠,特定回复前缀、转发外壳、尾部标记与方括号块可能被删除。联网与离线实现都必须使用同一算法,以避免同一邮箱出现两个视图。这解决的是一致性,不是含义认证。RFC 还明确提醒:如果被删掉的文本本来有实际意义,排序就可能出错。

REFERENCES 在不完整声明上重建结构

REFERENCES 更接近人们想象中的会话血缘。它优先读取 References 中的 Message-ID;在特定条件下,退回到 In-Reply-To 中第一个有效 Message-ID。带引号和不带引号但等价的标识会被归一化,父子连接也不得形成环。

现实数据并不整洁。没有有效 Message-ID 的邮件会获得一个仅供算法使用的唯一标识;多个邮件重复使用同一 Message-ID 时,只有序列号最低的第一封保留它,其余邮件得到新造标识。引用的祖先在集合中找不到时,算法建立一个 dummy 占位节点,先维持结构再处理缺口。

随后,空的 dummy 可以被删除;带子节点的 dummy 可能被删除并把子节点提升一级;在根部提升会破坏形状时,dummy 又可能保留。截断的引用链导致冲突时,已有父关系可能被保留或拆除。形成环的连接被拒绝。

这些规则很严谨,却不是取证发现。占位节点不是找回的一封邮件,提升子节点不证明中间从未存在别的邮件,新造标识也不是经过认证的身份。它们只说明:在当前输入不完整或矛盾时,这套算法怎样得到一个可用结果。

主题回退还能连接头字段没有直接连接的根

Message-ID 树建好后,REFERENCES 仍会比较根节点的基础主题。相同且非空的主题可按规则合并;有时选择非回复邮件作为代表,有时创建新的 dummy 父节点。

所以,界面上的两条连线可能看起来完全相同,来源却不同:一条是邮件直接声明的引用,一条来自多级标识链,一条只是根主题相同后的合并。如果边没有附带来源,复核者无法知道它表达的是头字段声明还是算法回退。

RFC 5256 自己指出,假的 References: 数据会把一个线程错误并入另一个线程。Message-ID 归一化能避免书写差异造成误判,却不能认证是谁写入头字段,更不能证明其叙事真实。

“按日期”也不是单一时钟

SORT 同样先检索,再按给定条件排序。字符串依照活动排序规则,日期与大小遵循定义的升序;显式条件完全相同时,邮箱序列号成为隐含最终条件。正因如此,REVERSE SUBJECT 并不等于把 SUBJECT 的完整结果倒过来,隐含序列号不会随之反转。

DATE 条件首先读取邮件的 Date:,换算到 UTC;无效时区、时间或日期会按规范补值。如果发送日期无法解析,则使用服务器的 INTERNALDATE。同一列“日期”因而可能混合发件方声明、规范修补值与服务器持有的到达元数据。

RFC 5957 后来增加按 From/To 显示名排序。它会使用解码后的完整显示名,缺失时再退到邮箱与主机信息,却不会擅自猜测某种语言里的姓氏。这个克制很重要:排序应声明自己的键,而不是伪装成普遍的人类姓名秩序。

可更新视图仍不是审计账本

UID 相比序列号更适合作为标识,但 UID 仍需与邮箱及 UIDVALIDITY 共同保存。RFC 5267 可以在邮箱变化时更新搜索或排序结果,RFC 5182 可以保存结果集供后续命令复用。这些机制提升效率和实时性,并没有把视图变成不可篡改的历史。

若业务判断依赖会话血缘,应保留:邮箱快照或状态令牌、查询条件与字符集、线程算法、活动排序规则、服务器能力及版本、UIDVALIDITY 与 UID、相关原始头字段、归一化 Message-ID、每条边使用的规则、dummy 的建立与剪枝、主题合并、平局条件、结果哈希以及客户端渲染版本。

结论也必须写在正确层级。“本次快照的 RFC 5256 REFERENCES 视图把 B 放在 A 下面”可以复核。“B 是为了回复 A 而写”需要额外证据。“收件人已经阅读并接受 A”更不是这棵树能够证明的事。

邮件线程值得使用。真正的风险,是让一个为了浏览而设计的视图替代它没有资格认证的历史。

来源