PR-16 · Discord 与 Notion 两个采集渠道 - #178
Open
titanwings wants to merge 5 commits into
Open
titanwings wants to merge 5 commits into
titanwings wants to merge 5 commits into
Conversation
重建说明:本提交由会话转录重建,提交信息取自原分支(ds/16-discord-notion)。 文件内容为集成分支上的最终态,不是当时那一刻的中间态——原分支的 per-commit 文件树随 /tmp 清空丢失,转录只保留了提交信息与 git add 的路径清单。 原提交信息:(未记录)
titanwings
force-pushed
the
ds/15-identity
branch
from
September 15, 2026 09:36
3c369f8 to
786e8c6
Compare
titanwings
force-pushed
the
ds/16-discord-notion
branch
from
September 15, 2026 09:36
70c08eb to
3b65daf
Compare
这 19 个 PR 是堆叠的,base 是上一条 ds/* 分支,而 CI 的触发名单只有 [dot-skill-test, dot-skill, main],所以本分支的 push / PR 都不会触发工作流, PR 页面永远显示 no checks reported。这里只改触发条件,不动任何产品代码。
CI 从建立起**没有一次是绿的**(30 次运行全部 failure),原因有两个,都不是产品 代码的问题,而是门禁自己坏了: 一、`npm test` 在 Node 20 上根本跑不起来 "test": "node --test \"tests/*.test.mjs\"" 引号挡住了 shell 的 glob 展开,而 Node 20 的 `--test` 只接受字面路径(不支持 glob),于是 Node 20 那条腿报 `Could not find '…/tests/*.test.mjs'` 直接退出; Node 22 支持 glob,所以本机永远是绿的。矩阵里同时有 20 和 22,CI 必红。 更要命的是有一条测试**把这个坏字符串钉死了**: `assert.equal(manifest.scripts.test, 'node --test "tests/*.test.mjs"')`。 断言一条命令的**文本**,不等于断言它能跑 —— 这正是它没拦住的原因。 改成 `node --test tests/*.test.mjs`(不加引号):shell 先展开,Node 20 拿到真实 路径;Node 22 拿到同样的路径也认。(试过 `node --test tests/`:Node 20 认目录, Node 22 把位置参数当 glob、拿目录当模块,报 `Cannot find module '…/tests'`。) 新测试改为断言:命令不是裸 `node --test`、参数里没有引号、**每个参数都能对上 真实存在的文件**。 二、目标审计在 CI 里直接崩,而不是报告 推送行的 `rev-parse --verify --quiet` 包在会抛异常的 `git()` 里:ref 不存在时 git 以 1 退出且无输出,于是这一行 —— 本该报告"这个分支不在任何远端上"的那一行 —— 把整个审计抛崩(CI 日志里 stdout/stderr 全空)。CI 的 checkout 本来就没有 `origin/dot-skill-test` 跟踪 ref,所以每次必崩。 改动:ref 探测用不抛异常的 `gitRef()`;CI 环境里这一行记为**缺口**并说明理由 (CI 的源码本来就来自远端,这条要求在这里无法验证),而不是假装验证过;本地 仍严格,且把"工作树干净"这句标题真正纳入判定(此前标题这么写,检查只看 ahead,脏工作树照样绿)。 三、验收红行不再空着理由 visual-check 缺少 playwright 的提示打在 **stderr**,而这一行只引用 stdout, 于是失败信息是空的。现在缺 playwright 时单独报一行并给出该设的变量。 本地核对:Node 20 与 22 都收集 383 项。 (cherry picked from commit 2c90893)
CI 终于能跑测试之后(前一个提交修好了 npm test),Node 20 与 22 两条腿都稳定
红在同一条上:
not ok 123 - cli: --mode mcp routes to the MCP client and needs a target
本机复现的办法很直接 —— 把 `~/.colleague-skill/` 移走,这条立刻变红。原来
`credentialPaths()` 除了 `DISTILLY_HOME` 还会回落到 `~/.colleague-skill/<file>`,
而 feishu-mcp 的两条 CLI 路由测试调 `runCollectCli(..., { transport })` 时不传
env,于是 `loadCredential` 拿的是 `process.env` —— **我笔记本上那份改名前的旧
凭据**。CI 上没有那个文件,所以必红。同一个文件里"缺凭据要响亮失败"的那条测试
早就为此把 HOME 指到空目录并写了注释,这两条却漏了。
改动:
1. `tests/feishu-mcp.test.mjs` 的 sandbox 现在把凭据写进自己的 home,并带一份
`{ DISTILLY_HOME, HOME }`;两条路由测试显式传它。移走真实旧凭据后 8/8 通过。
2. `src/collect/slack.mjs` 的 `credentialPaths()` 改为 `env.HOME ?? homedir()`,
与 `kit.mjs` 一致:旧路径回落必须跟着被覆盖的 HOME 走。此前它无视 `env.HOME`,
一次"隔离"的运行照样能读到真实 home 的凭据,两个渠道对"刚读的是哪个文件"
也会给出不同答案。
3. `tests/collect.test.mjs` 里两条只会打印 `false !== true` 的断言补上现场信息
(收据、stderr、work 目录清单)—— 这条 CI-only 失败此前从日志里根本
看不出是"没写文件"还是"写到别处去了"。
全量:无真实旧凭据时 383/385(另 2 条是本机未提交/未推送时的审计行)。
(cherry picked from commit 2551c9b)
CI 现在给得出失败现场了,于是这条一看就清楚:
the page was not written; receipt={… "path":"…/knowledge/raw/slack/c0123-p001.json" …}
tree=knowledge/index.json, knowledge/raw/slack/c0123-p001.json
文件就在那里,只是名字是小写。`writeRaw` 会把名字过一遍 `slug()`,所以频道
`C0123` 落盘成 `c0123-p001.json`(收据里的 `channel_id` 仍然是 `C0123`)。断言
写的是 `C0123-p001.json`,在 macOS 上 APFS 大小写不敏感所以"通过",在 Linux
runner 上就红了 —— 又是一条"取决于在哪台机器上跑"的测试。
断言改成真实文件名,并在注释里写明为什么是小写,免得下次又被"修"回去。
(cherry picked from commit a467934)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
本 PR:
ds/16-discord-notion→ds/15-identity,1 个提交,321 insertions(+)。本 PR 的提交清单
PR-16 · Discord 与 Notion 两个采集渠道
ds/16-discord-notion(本地,已合并进dot-skill-test)src/collect/kit.mjs、src/collect/discord.mjs、src/collect/notion.mjs、src/parse/chat.mjs(透传 method / 凭据来源 / 认author.username)、src/commands/credentialed.mjs、tests/collect-discord-notion.test.mjs1. 为什么
CONTRACT.md§1 的采集命令行写着 8 个渠道,此前只实现 4 个(飞书/Slack/钉钉/X)。未实现的两个渠道此前回
collect/planned-channel并写明所需凭据——诚实,但仍是缺口。这一轮把 Discord(bot token)与 Notion(内部集成 token)补上,
PENDING_CHANNELS从 4 条缩到 2 条(reddit / gmail)。
2. 三条纪律,与已有渠道一致
discord_config.json/notion_config.json);redact/scrub覆盖回执的每个字段(含嵌套数组)knowledge/raw/<channel>/,同一次采集把它变成带锚点正文(Discord 交给已有的导出解析器,Notion 走段落化),一页/一页一个账本条目分页与限流:Discord 用
before游标(一页不足 page size 即结束),429 读 body 里的retry_after(秒)退避——测试断言睡的是 1500ms;Notion 用next_cursor。两处都支持断点续采(游标写在
$DISTILLY_HOME/state/)。3. 顺带修好的两处解析器透传
parseChat现在尊重调用方的method与credentialed/credential_source/credential_file:导出来的是
user-export,API 采集来的是api-bot-token——否则采集来的页面会在账本里冒充用户导出。
author.username(导出用name,API 用username):同一批消息无论是下载的还是采集的,人名一致(
林工而不是u1)。与其它格式一致;
--source chat可恢复旧名。4. 怎么验
测试覆盖:两页分页(游标串接 + 逐字字节 + 锚点正文 + 一页一条目)、429 退避毫秒数、
缺凭据零写入、写动词被拒、Notion 页面 → 段落(跳过块点名)、Notion 白名单(两个 POST 放行、
POST /v1/pages与PATCH拒绝)、页面 id 从 URL 解析与坏 id 响亮失败、空页面不算空文档、kit 的脱敏与凭据查找(环境变量 / 配置文件 / legacy 路径 / 非法 JSON)。
5. 已知缺口
PENDING_CHANNELS,doctor会列出)。POST /v1/databases/{id}/query,但 CLI 尚未暴露--database-id)留待后续。