 xcsandClaude Fable 5
|
f70de44338
|
终审残余两处:换 address 就是换信箱;错误话的出口也剥 NUL
复审的两条范围外观察,都落在这一轮刚动过的那两段里,一并收掉:
① 收侧判据看不见 address —— `imapSettingsOf` 用的是 `login ?? address`,而 login **常常空着**,
这时候真正决定「登到哪个信箱」的就是 address。上一刀把发信三键排除掉之后,
换 address 判据为假:旧游标的 UID 说的是原来那个箱子,而 UID 是按信箱编号的。
判据改认**实际登录名**:login 填了就比 login(这时 address 只是发信落款,换它不动收信),
空着就比 address。wecom 不参与 —— 它的 address 由探测回填,比它会在回填那一刻误清游标。
② 治毒信的那道门自己会被同一个字符毒死 —— 降级存档把解析失败的原因写进正文与
`last_poll_error`,而那串原因常常来自那封信自己的字节。`errorText` 出口一律剥 NUL:
所有落库的错误话都从这一个出口出去,分散到每个调用点去剥,总会漏掉下一个新调用点。
两条测试都验过 RED→GREEN(拿掉修复各自变红,旁边的既有用例不受影响)。
顺带:`mail/CONTRACT.md` 两处点名的 `createFakeMailProvider` 全仓不存在(未定义、未导出、无调用方)——
契约文档摆一个不存在的出口,照它接线的人会去 import 一个没有的东西;改成实情。
debt 补 ⑳-㉔(IMAP 每轮重登录与腾讯频率限制 / wecom -1 无退避 / pollDue 每 tick 刷日志 /
notes 不设上限 / claimForResend 推 created_at 使该列不再是创建时刻)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 04:07:08 +08:00 |
|
 xcsandClaude Fable 5
|
58b1111e7e
|
终审修复轮补测:翻页上限与 lowestUid 各钉一条(P1-1 / P1-2 原本只改了实现没有测试)
- wecom 翻页上限:服务端一直回 has_more:1 时不再死循环 —— 到顶交出手上这些、more=true、游标原样退回。
单请求 30s 超时护不住它:每一页都是成功返回,超时永远不触发。
- imap 回填锚点的取最小抽成纯函数 `lowestUid`(原先内联在 withImap 里,测不着):
测里拿二十万个 UID 反证 `Math.min(...uids)` 真的抛 RangeError,而 reduce 那一版稳。
- 顺手清掉 mail-service-poll.test.ts 里最后一处指向「修复报告」的注释(语境会丢,P1-6 同款)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 03:49:57 +08:00 |
|
 xcsandClaude Fable 5
|
816389a185
|
邮件收发终审修复轮:token 按凭证指纹、毒信有退路、只改发信设置不丢信、重发先抢、列表不搬原件、save 不盖运行列、重锚出声、脏游标不炸页、线程口径归一
全分支终审(跨任务视角)逮出的十条 P0 + 八条 P1,一次修完。每条都配了测试。
P0-1 企业微信 token 缓存改按**凭证指纹**(sha256(corpId,corpSecret),secret 不进 Map 的键):
按 account.id 存有两条真实串号路径 —— 草稿探测共用 'draft'(探完 A 公司再探 B 公司会命中 A 的
token,回的是 A 的应用邮箱地址,凭证填错的 B 也显示成功);已存账号换了凭证 id 不变,
下一轮轮询继续拿旧 token 拉旧 corp 的收件箱,分发给新账号绑的通道(窗口 ≤ 7200s)。
P0-2 一封「毒信」不许卡死整条路:①domain 的 buildInboundRecord 把落 text/jsonb 的字段一律剥 NUL
(PG 拒收它,MIME 解出二进制垃圾时真会出现);②单封失败先落**降级骨架**(原件原样存、
主题从原始字节扒、正文写明失败原因)→ 落成了就推进游标并把「第 N 封(providerRef)解析失败,
已降级存档」写进 last_poll_error;骨架也落不下才停在这一封,那时 error **点名是哪一封**。
降级行不自动分发(正文是失败原因,不是客户的话)。
P0-3 cursorReset 只比**收侧**键(imapHost/imapPort/imapTls/login/folder;corpId/apiBase):
从前比全部 settings,把 465 改成 587 也会清游标 → IMAP 重锚 → 上次游标到重锚之间的来信永远不拉。
P0-4 复用旧行重发**先抢**(OutboundStore.claimForResend,一条条件更新;真 store 与内存件同款):
两个后端连同一个库,两台同时重放同一次放行都判「超时可复用」→ 两封真邮件,破掉「同键不发两封」。
P0-5 列表与只要元信息的读**不取 raw**(一封上限 16MB × 200 行);rawAvailable 由 `raw is not null`
投影,不靠取回字节再判;只有「下载原件」走全列的 get()。InboundRecord 新增 rawAvailable。
P0-6 账号 save() 只写**配置列**:运行列(cursor/last_polled_at/last_poll_error/last_inbound_at/
polling_until)只经 savePoll/claimPolling/releasePolling 改 —— 整行 upsert 会把另一台正持着的租约
与刚推进的游标盖回读那一刻的快照(08-24 loop_state 整份覆盖同款指纹)。清游标改为显式单写那一列。
P0-7 UIDVALIDITY 变化不再静默:provider 回 note,用例落进 last_poll_error(「已重锚在当下;
期间来信未拉取」)—— 丢了一段信而账号页一片绿是界面撒谎。
P0-8 parseCursor 连取值范围一起查(非负安全整数 + 界内:UID ≤ 0xFFFFFFFF、秒 ≤ Date 上限)——
1e13 正是安全整数,乘 1000 出界,一行脏游标能让 listAccounts 整页 500。debt ⑨ 划线销账。
P0-9 线程口径归一:threadIdOf 改成**取根**(references[0] ?? inReplyTo,ADR-013 决策 3),
mail-wiring 调它、不再内联一份。原测试改断言,不删。
P0-10 文档说老实话:mail.send 工具路发出的信**不登记 outbound_ref**(recordOutbound 的唯一调用方
channel.sendMail 在 src 里零调用方)—— API 文档 §4 与 ADR-013 决策 6 各补一段,根治记 debt。
P1:imap anchor 的 Math.min(...uids) 改 reduce(十万级 UID 会栈溢出)· wecom 翻页加页数上限
(服务端一直回 has_more:1 会死循环)· MAIL_TOOL 导出并对**真契约**断言(原 v1/v2 是手抄夹具、
自证,且 v1 版本号写错)· reset-runs 清运行数据时把邮箱账号的游标与拉取状态一并清(留着游标而信没了,
wecom 十分钟重叠窗会把最近的信当新信再投一次)· due.test 的 leaseMsFor(30) 断言改写死 300_000(原为自证)·
cursor.test 删掉指向评审报告的注释 · 设计稿稿头标注三处已被 ADR-013 取代 · ADR-013 补记 wecom
compose_send 不带 inReplyTo/references(它发出的信在对方那里不串线程)。
契约变更(§12.4):port.ts 的 InboundStore 加 getMeta、OutboundStore 加 claimForResend、
savePoll 的 cursor 允许显式 null、MailProvider.pull 回参可带 note —— 实现方只有本模块 adapter 与
__tests__/fakes.ts 的内存件(两边语义一致,已同改),CONTRACT.md 已同步,api-report 已刷新。
测试:1922 → 1942(mail 模块 102 → 113,含真 PG 的 save 不盖运行列 / 不搬原件 / 两条真连接抢同一行)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 03:47:48 +08:00 |
|
 xcsandClaude Fable 5
|
7b79d542a8
|
修复轮 1(wecom pull):游标推进与 more 改由同一判据决定,治单页 has_more:0 也被 limit 截断时丢信
评审逐字节比对确认 wecom-provider.ts 与 brief 转录一致——问题在计划本身:`exhausted` 只说明
页翻完了,而 LIST_PAGE(100)与 opts.limit 无关,最后一页可能同时 has_more:0 且新信多过本轮预算,
此时按 exhausted 推进游标会把 wanted 截掉的那几封永久丢弃(重叠窗只兜 10 分钟)。
改法:先算 more = !exhausted || fresh.length > wanted.length,再据同一个 more 决定 cursor 是否
推进到窗口上沿——游标与 more 从此不会分裂成两个判据。补两条测试:①单页 has_more:0 但新信
多过 limit 时游标原样退回(改前 RED:游标被错误推到 endTime,改后 GREEN);②-1 连续繁忙到
边界时 get_email_alias 恰被调 4 次(首次 + BUSY_RETRIES 3 次),此前已隐性通过,补断言钉死。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 02:09:06 +08:00 |
|
 xcsandClaude Fable 5
|
d64f0b5ffd
|
修复轮 1(评审):mail 用例测试补钉三处伪覆盖 —— 附件不重存/中途失败/分发不因单个失败断链
评审判 Needs fixes:三条断言此前恒真,改坏实现照样绿。①重复拉到里撞 providerRef 的
那封改带附件,才验得出「守卫在存附件之前生效」;②拉取抛错改为两轮 pollNow ——
第一轮先用 afterCalls 让 pull() 真正成功推进游标,第二轮才炸,不再是「零封被处理,
游标没动只因为它压根没机会动」;③分发失败的订阅方 boom 从最后一个挪到中间(ok →
boom → skip),证「继续往下投」而不是恰好排在末尾。send 测试补 references 断言。
fakes.ts 的 failPullWith 加 afterCalls 支持第 N 次调用起报错。逐条用临时注入的
实现缺陷验证测试确实会红(验证后已还原,mail-service.ts 净差异为零)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 02:06:42 +08:00 |
|
 xcsandClaude Fable 5
|
5bd92f2e93
|
mail adapter:企业微信邮件 API provider(token 缓存与刷新 / 翻页 / 已知 mail_id 不再读 / EML 编码兜底 / compose_send)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 01:50:11 +08:00 |
|
 xcsandClaude Fable 5
|
7afc40987b
|
mail adapter:IMAP/SMTP provider(只读打开 / UID 游标 / n:* 过滤 / UIDVALIDITY 重锚 / nodemailer 发信)+ env 门控真信箱 IT
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 01:47:24 +08:00 |
|
 xcsandClaude Fable 5
|
40e592ae94
|
mail 用例测试:发信留痕 / 厂商失败不抛 / 幂等键 / 附件来源 / from 对账号
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 01:39:06 +08:00 |
|
 xcsandClaude Fable 5
|
fba82c71dc
|
mail 用例测试:拉取 / 去重 / 逐封游标 / 失败停位 / 分发三态 / 重新投递 / 附件原件
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 01:38:01 +08:00 |
|
 xcsandClaude Fable 5
|
78e36ab73c
|
修复轮1:凭证解不开时明文经 SyntaxError 泄进 last_poll_error;同键在飞的那封会被当失败行重发;发成功不清上一次的错误话;卡死的 dispatching 给人手一条路
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 01:29:24 +08:00 |
|
 xcsandClaude Fable 5
|
79fd25e1c1
|
mail 测试夹具:内存 store 排序与真 store 严格同序,补两处差异注释
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 01:11:48 +08:00 |
|
 xcsandClaude Fable 5
|
1328aeb142
|
mail 用例:账号建改删与探测(草稿探测可借库里密文;wecom 探到地址回填);收发用例同份落地待测
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 01:08:48 +08:00 |
|
 xcsandClaude Fable 5
|
b9a4af9a2a
|
mail adapter:postal-mime 解析器(GBK 字头 / 群组地址 / References 拆串 / 附件字节)+ 测试夹具
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 00:59:40 +08:00 |
|
 xcsandClaude Fable 5
|
d65579c0d8
|
修复轮1:mail 账号列表排序键还原为 name(与 channel/llm 惯例一致),测试改断言集合而非顺序
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 00:53:28 +08:00 |
|
 xcsandClaude Fable 5
|
5f1e64b4d4
|
迁移 0033:邮箱账号 / 入站 / 投递 / 出站四张表;mail store 与跨进程租约(两连接对抢 20 轮每轮一个赢)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 00:49:24 +08:00 |
|
 xcsandClaude Fable 5
|
8d7e56aa92
|
mail domain:发信校验与选账号(from 不静默回落)、分发状态机、账号记录与视图(密文进不了视图)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 00:28:02 +08:00 |
|
 xcsandClaude Fable 5
|
6748ef01b7
|
mail domain:解析结果 → 入站记录 / 视图,附件与原件按上限分存留注,厂商原话脱敏三形
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 00:26:08 +08:00 |
|
 xcsandClaude Fable 5
|
83f65b2785
|
mail 模块骨架:契约 / 端口 / 领域错误 / 游标两形 / 到期与租约(ADR-013)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TM4kUBvekALu9qqmp7D3wr
|
2026-08-26 00:11:14 +08:00 |
|