任务:个人知识与个人记忆的双向同步——Hub 端(决定 131 第四阶段之一)
依据:
- 决定 131,所有者原话:“两个都放,前提是circle支持知识库,也支持知识库同步,同步是hub和circle双向的(多客户端可共享,用户自己选择方式)”。
- 决定 135:记忆纳入。个人记忆随本人的各个 Circle 同步,团队记忆供全体成员的 Circle 加载。
- 设计页
knowledge-workflows.md中的“同步(R7)”与“分阶段”第 4 项。
前提:
- 第二、三阶段已合并(PR #128、#129):空间、授权、统一条目、提交共享和个人工作流都在 Hub 上。
- 页面重做与记忆(PR #132,
kw-console.md)提供了记忆的存储与读写协议:get_nodes.memory、upload_file.memory。
协议现以中立规范 docs/protocols/knowledge-sync-v1.md 为准(决定 137,2026-11-03);本页是实施记录,下文的提案、审查与验证保留供追溯,与规范不一致之处以规范为准。
本任务做 Hub 端的同步接口。Circle 端在 Circle 仓库另立任务,按本任务写进合约的接口实现;两边共用同一组测试样例。
做法
- 分支
kw-sync-local,工作树circle-hub-sync,从页面重做分支kw-console-local(4d2056c6,草稿 PR #132)开出,因为要用到其中的记忆存储;#132 合并后再变基到 main。只在本机提交,Claude 审查后推送、开 PR。 - 先在本页“实施方案”一节写出接口与数据结构,经 Claude 审查后再动手。
- 规则与验证同前几个阶段的任务页:
- 接口、记录类型、拒收码与词表写进合约并生成;
- 新增配置变量要同步登记到平台的转发清单;
- 迁移只追加,并复核固定的源文件摘要;
- 每步跑受影响的测试,包括
hub/platform/environment.test.ts、hub/refs/diagnostics.test.ts,以及带 PostgreSQL 18 的 ownership 与 lock-namespaces 测试。
- 完成后在
docs/hub-r4-progress.md记为决定 138(Hub 端同步的实施,依据决定 131、135)。 - 界面用语:一律用主流软件的说法(例如“同步方式”“仅保存在本机”“同步到 Hub”“有冲突”“上次同步”),不出现哈希、指针、版本号等系统内部概念。界面用语写进模型词表,不在控制台手写。
要做到的(验收标准)
- 同步接口:
- 用专门的 HTTP 接口(例如
/api/sync/...)。Circle 用同一个成员令牌认证,包括经 MCP 标准授权取得的令牌和访问令牌。 - 不新增 MCP 工具:MCP 仍是六个工具,模型看不到同步接口,也不能在任务中借它绕过“按地址读取”。
- 接口和样例写进合约。
- 用专门的 HTTP 接口(例如
- 同步范围:
- 调用者本人的个人空间:知识条目、原文、文件夹,以及个人记忆。
- 个人工作流和通用技能本阶段只同步“清单与版本”,安装仍走现有流程。
- 他人分享给本人的内容和团队内容不走同步,用现有读取接口。
- 团队记忆只读加载,走
get_nodes.memory的会话模式。
- 同步语义:
- 内容寻址:列出对方缺少的对象,分批上传、下载,并校验哈希。
- “当前版本”的移动用比较并交换。已被别的客户端推进时拒绝,并返回最新状态,由客户端合并后重试。
- 合并:两边都有新版本时两者都保留。同一条知识或同一篇记忆被两边分别修改时,标为“有冲突”,不按“后写覆盖”取舍。本人选定其中一个,算一次新的修改。
- 每次同步写入记录:谁、哪台客户端、补齐了多少对象、版本从哪到哪。
- 核验:
- 上传的知识条目由 Hub 重新核对引文与路径,不采信客户端的“本机已验证”。核对不一致的条目记为“待验证”,并告知客户端。
- 上传的记忆按记忆的规则做凭据扫描与大小检查,沿用 #132 的限额。
- 同步方式:
- 同步方式(仅保存在本机、同步到 Hub)由本人设置,Hub 记住它,并在个人空间和团队页的成员详情中显示“同步方式”和“上次同步”。
- 从“同步到 Hub”切回“仅保存在本机”时,Hub 清除本人个人空间的内容(知识与记忆),记录只留哈希,相关分享随之失效。页面提示本机副本不受影响。
- 容量与限流:同步写入计入个人空间容量(默认每人 1 GiB);单次请求与批量大小有上限,超出时给出明确的拒收码。
- 规范形式:哈希前的规范形式写进合约:换行统一为 LF、去掉 BOM、Unicode NFC。两边用同一组样例核对,避免 macOS 与 Windows 上算出不同的哈希。
- 测试:
- 两个客户端交替离线修改、合并与冲突、并发写入时的版本竞争、断点续传、哈希不符;
- 读写他人空间时,回应与对象不存在相同;
- 切回“仅保存在本机”后内容被清除,以及容量超限;
- 记忆的同步、冲突和凭据拒收;
- 给 Circle 端用的共享测试样例:合成的知识、记忆、对象与期望的同步结果,放进合约测试目录。
不在本任务
- Circle 端的本机知识库、本机记忆与同步客户端:在 Circle 仓库另立任务(决定 139)。
- 上传署名、原文归档、成员停用与删除:第五阶段(决定 140)。
实施方案
1. 状态、范围与审查边界
第 1~12 节是 Codex 2026-11-02 的原始提案,现经第 13 节审查通过;与第 13 节冲突之处以审查决定为准,原提案保留供追溯,不表示接口已经上线。当前分支基于 4d2056c6,任务页提交为 6b120740;本步只改任务页,不改模型、代码、迁移、Circle 仓库或部署。决定 138 待实现完成后按本页来源记录,不提前把建议写成所有者决定。
同步服务放在 hub/knowledge/sync/,HTTP 路由放在 hub/server/,私有存储与派生状态由 hub/records/ 提供端口。包之间只经各自 api.ts 调用。同步是本人空间的传输与版本管理,不是运行输入读取、模型工具或团队发布通道;不调用模型,不执行客户端代码,不访问业务系统。
知识、原文、目录、记忆共用一个个人同步快照,避免知识已移动而其原文尚未保存的半次提交。个人工作流/通用技能只纳入清单目录,不上传可执行文件,不因同步而发布、安装、授权、分配或获得业务系统能力。团队与获授权的他人内容只保留既有读取地址,不通过同步 API 导出其字节。
方案中需要 Claude 重点确认的存储清除、原文规范形式、已有账户默认方式与团队副本边界列在第 12 节。实现前须完成这次审查;不能把这些建议当作已经获得的例外许可。
2. HTTP 接口与身份
统一前缀暂定 /api/sync/v1,请求与答复均有独立的版本化合约;不把它们并入六工具的输入 Schema、工具列表或模型提示。结构和错误码先登记合约,再生成运行时类型。路径中的会话与操作 ID 都是有界的不透明标识。
| 方法与相对路径 | 输入与输出 |
|---|---|
GET /state |
返回本人的同步方式、清除状态、当前状态标识、同步快照、容量、实际批量限制及上次成功同步时间;空空间不因读取而创建正文 |
POST /clients |
幂等登记本人客户端的随机 ID 与受限显示名称;返回服务器绑定的客户端标识 |
POST /sessions |
输入客户端、已知状态及基准快照,建立有期限的续传会话,冻结下载清单;返回当前状态、清单游标和限制 |
GET /sessions/{id}/inventory?cursor=… |
按冻结快照分页列出本人对象的 ID、类型、大小与关系;游标绑定本人、客户端、空间代次和快照 |
POST /sessions/{id}/missing |
分批提交本机对象 ID,返回 Hub 在本人当前代次中尚未拥有的 ID;不能探测全库是否已有相同字节 |
POST /sessions/{id}/upload |
输入操作幂等键及对象批次,检查结构、大小、摘要和凭据;整批接受或整批拒收,返回已接受对象及续传进度 |
POST /sessions/{id}/download |
输入对象 ID 批次,按大小上限返回本人对象;不接受来源 URL、本机路径或任意 rec: 读取请求 |
POST /sessions/{id}/commit |
输入操作幂等键、期望状态、共同基准与候选快照,重新核验后比较并交换;返回当前状态及有界的核验结果/冲突分页入口 |
GET /operations/{id} |
取本人某次批次/提交/切换的安全回执,解决提交成功但应答丢失;不知道或不属于本人时统一不可用 |
POST /sessions/{id}/abort |
结束本人续传会话,释放只属于它的暂存引用;不删除其他会话或当前版本使用的对象 |
POST /mode/check |
本人查看切换影响:个人内容量、将失效的授权及共享数量、本机副本不受影响;不写入或清除内容 |
POST /mode |
输入目标方式、期望状态、操作幂等键及明确的清除确认;切换方式并返回结果,清除未完成时据实返回进度 |
- 每个请求使用现有成员 Bearer 令牌验证,兼容现有 OAuth 取得的令牌与直接访问令牌;复用当前撤销、到期、停用检查,不另建一套账户。控制台仍只用用户名和口令登录;同步接口不接受控制台 Cookie、设置码或控制台会话令牌。需要的令牌种类/客户端信息通过 records 的认证端口取得,不在路由复制鉴权 SQL。
- 所有空间都从当前账户推导,不提供可写的
owner/space参数。管理员同样只能同步自己;猜到他人的对象、会话、操作或快照,状态码和正文与该路由中的不存在情形相同。直接取对象/会话等统一为中性的 404/SOURCE_UNAVAILABLE,候选图的依赖缺失也不能区分未上传与他人持有。身份无效统一 401,在检查内容和解码大正文前拒绝。 - 客户端 ID 是账户内的设备标识,不能代替身份;OAuth 客户端等由服务器确认,设备名称与随机 ID 是客户端声明,两者在审计中区分。用新客户端 ID 或换令牌不能绕过按账户累计的限流。
- 沿用服务器的 Host、Origin、TLS、请求总量与关闭约束;不新开监听、不开放 CORS。请求和答复使用
Cache-Control: no-store,日志不记令牌、对象正文、标题、目录或引文。API 不在 MCP 中暴露,不提供在任务里调用同步路由的代理工具。
3. 对象、稳定身份与快照
不拿现有记录摘要充当跨客户端可改对象的稳定 ID。每个目录、知识条目、原文和记忆文档有稳定的随机 item_id;一次修改生成不可变修订对象,包含本项 ID、父修订及内容。重命名、移动和修改沿用同一个 ID;同一文本的两个独立对象仍可有不同身份。既有对象导出时由持久映射分配 ID,不能每次导出重编号。
拟在 contracts/src/sync.ts 定义封闭对象族,由合约生成对象种类:
| 对象 | 内容与边界 |
|---|---|
| 原文字节块与原文清单 | 大文件按不超过 1 MiB 的块切分;清单保留顺序、总字节数、媒体类型、原始字节摘要和文件元数据。整体仍受现有原文大小限制,分块不能绕过它 |
| 知识修订 | 现有条目内容、目录 ID、依据与逐字引文、父修订;客户端的核验标记不产生任何资格 |
| 目录修订 | 名称、父目录 ID 与父修订;根目录唯一,拒绝环、越界与名称归一化后的同级重名;空目录也同步 |
| 记忆修订 | 标题、目录、Markdown、启停及父修订;与知识条目分开,不产生输入或依据引用 |
| 包清单对象 | 已有 pack.json 的清单/版本信息及其摘要,区分本人已有安装与仅有清单;不含代码、不把“持有清单”当作已发布或可运行 |
| 清单页与快照 | 按类型和稳定 ID 排序的对象头列表,单项可有多个冲突头;分成有界页,以根清单引用各页,不把整个空间装进一个记录或 HTTP 正文 |
协议结构拟固定为以下字段;这里是待审结构约定,实际正反样例在合约提交时给出可运行的完整值:
- 对象封套:
{schema: "personal-sync-object@1", kind, body}。修订对象的body含{item_id, parents, directory_id, content},目录等按具体类型使用严格 Schema;原文字节块的body为{bytes_base64}。对象 ID 为该类型封套的规范 UTF-8 字节的 SHA-256,传输描述符为{id, kind, bytes}。 - 物品索引行:
{kind, item_id, heads};快照含排序后的清单页引用及父快照,页不能重复拥有同一物品。实体修订祖先图与快照祖先图分别校验,不能把前者的摘要当作后者。 - 上传批次:
{schema: "personal-sync-upload@1", epoch, operation_id, objects: [{id, bytes_base64}]};服务器严格解码并解析每个对象封套,描述的类型和大小必须与实际一致。回执含批次请求摘要、接受/已有的 ID、计量与下一步,不保存原始请求副本。 - 提交:
{schema: "personal-sync-commit@1", epoch, operation_id, expected_state, base_snapshot, snapshot}。成功回执含新状态、知识与记忆投影的内部映射、核验/冲突计数及分页游标;失配回执含本人最新状态,不能自动把expected_state换成它再执行。 - Hub 核验观察另存
{object, rule_version, scanner, parser_lock, status, codes, checked_at},不嵌回客户端对象改变其 ID;具体代码与资格来自生成合约。上次成功同步仅在commit成功后更新,上传、探测、冲突拒收或部分失败不更新该时间。
删除、移动与冲突解决也生成修订。删除必须有明确的删除修订和父版本;缺少清单项不能解释成删除,上传半份清单不能静默丢掉其他客户端的内容。目录被一侧删除、另一侧增加子项,或移动后产生同级重名,作为冲突保留,不任意改名或删除后者。
每次从“仅保存在本机”开启“同步到 Hub”有新的不透明 epoch(空间代次);保持原方式或成功续传不另建代次。所有会话、拥有关系、当前头、客户端操作与游标都绑定账户和该代次;它不是用户界面概念。对象 ID 相同不代表有读取权,不能凭一个已知摘要认领他人的对象。新代次不能自动恢复旧代次授权、旧令牌之外的权限或清除前的个人数据;本人明确选择重新同步后,可重新上传本机副本作为新代次的初始内容。
4. 批次、续传与原子提交
- 客户端读取状态并建立会话,分页取得冻结清单,将本机与远端清单比较;调用
missing补齐 Hub 缺少的对象,用download获取本机缺少的对象。只在本人的当前代次查拥有关系,不给出全库命中、其他作者或去重节省量。 - 每批有
operation_id,相同操作和相同请求重放返回同一回执;同键异载荷返回既有IDEMPOTENCY_MISMATCH。一个对象在同账户/代次被多次接收只计一次;一批里有摘要不符、重复 ID 携带不同内容或超限时,整批不登记。流中断的临时文件不算已接受对象,已经确认的其他批次可继续使用。 - 上传只进入本人暂存区,尚不能被 MCP、搜索、记忆会话或授权者读取。先检查再登记,失败不把疑似凭据落成原件;临时写入、落盘、数据库登记各有故障恢复测试。哈希检查用于本任务明确要求的同步对象身份,不给无关正文另加校验流程。
commit检查候选对象图齐全、类型匹配、无环及大小有界,继而重验知识与记忆。持本人空间锁,重新检查账户、代次、方式、容量、依赖读权和期望状态,在同一事务更新知识、记忆、目录/包清单投影和同步头;不能逐条调用会各自推进当前版本的现有写接口,造成部分成功。- 文件须先完成原子落盘,事务才可引用;事务失败留下的是不可见、已计暂存额度的对象,之后可续用或清理。没有成功提交的批次不改当前版本。成功后即使网络应答丢失,也能从操作回执确定结果,不猜测远端是否成功。
- 续传会话默认 24 小时过期,返回确切到期时间。暂存对象在仍被任何活会话或已提交历史引用时不清理;放弃/过期后只回收无引用的暂存数据。过期会话拒绝继续提交,客户端保留本机修订并重建会话,不声称未提交数据永久保存。
5. 比较并交换、三方合并与冲突
GET /state 的 state_version 覆盖当前同步快照、知识当前版本、记忆各当前头、包清单、代次与同步方式;不含上次同步时间或心跳,避免同步本身制造无穷变化。现有控制台、MCP、共享引起的本人内容写入也要在同一空间事务更新这个状态,不能只有 HTTP 同步写入参与竞争。
提交必须同时带 epoch、expected_state 和 base_snapshot。基准只能是本人当前代次曾确认的快照;不接受他人根或任意记录作为共同祖先。Hub 比较并交换失败时返回 409、最新状态及清单入口,不移动头、不丢弃客户端已确认暂存的修订,也不自动重试并覆盖;客户端取得新状态后合并再提交。
两边共用确定性的三方合并规则,Hub 再检查候选是否合法:
- 只有一侧改动的对象取改动后的修订;不同对象的改动取并集;修订完全相同则合为一项。
- 同一稳定 ID 被两边分别修改,即使看似修改了不同字段,也保留两条头并生成“有冲突”的派生标记;删除对修改、目录位置竞争同理。不使用客户端时钟或到达顺序挑选正文,不做语义合并。
- 客户端不能把一个竞争头从候选快照中省略。新修订必须接续对应当前头;解决冲突时把全部竞争头列为父项,并明确选择内容或提交本人编辑后的内容,形成一次新修改。输掉的修订仍在历史里,不能伪装成已经合并。
- 冲突与引文核验是两个维度:各修订独立保留核验结果,冲突对象没有默认选中的正文。当前知识搜索/输入默认不使用未解决的对象;本人可显式查看历史,旧运行锁定的合法版本不因新冲突改写。记忆的冲突对象不自动加载,专用答复明确带冲突数量和对象,不能静默漏载;团队记忆的只读会话入口不变。
- 同步冲突不消耗运行模型修复次数,也不使用模型选版本。服务端计算冲突,不信任客户端传来的
conflicted:false。
6. 核验、来源与读取资格
沿用现有条目种类、内容和引用 Schema;每个知识修订都检查目录能否解析、依据当前是否对本人可读、解析器资格、片段位置及逐字引文。不能从客户端的 verified、旧回执或对象摘要推出已核验;解析器包只有清单而未安装时,同样不能声称核验完成。不得借核验自动抓取业务系统、导入命名文档或执行客户端脚本。
引文缺失/不一致等可修复情况保留为本人私有的未核验修订,返回有界的原因码与位置,不写成已核验或成功证明。存储状态沿用 knowledge_entry_status.unverified(现显示“未核验”,对应任务要求的“待验证”),不另造一个能绕过共享检查的资格。临时 I/O 故障与确定的不一致区分,前者写检查未完成;可以重交相同内容做新检查,但追加新的核验观察,不改旧对象。
只有在所有必需检查完成后才允许当前投影使用核验结果。依据、路径与正文在清单中缺失,或者仍有冲突时,不能成为新的已核验当前条目;共享与运行仍做自己的边界检查。对不可读的他人引用与不存在的引用保持同形,不在核验结果中透露另一成员是否持有某份原文。
记忆复用当前凭据检测、单篇 24,000 字节、每空间标题+目录+正文 48,000 字节及 20 篇的默认限额;拒收不保存正文,历史计入个人容量。冲突文档占一个逻辑篇数,当前额度按候选中最大的上下文字节预留,所有候选均计历史容量,不能靠冲突绕过限制;解决时持锁再查。记忆始终不作运行输入、依据或核验结果。
同步传输不改变原件的 read_tier、采集作者、个人凭据来源资格或上传声明。可同步的本人原件按原有上传安全规则处理,不能把只保留资料标成模型可读,也不能因在本机持有清单而取得团队解析器或外部来源资格。后续提交共享仍要求团队可用解析器与来源资格。
7. 规范形式与原文保真
新增版本化的同步编码规则,复用 records 的规范 JSON 编码实现;不修改现有 canonicalize 的全局含义,不重算已有 src:、条目、树根或记录摘要。
- 同步对象带类型与 Schema 标识,类型进入其摘要,避免相同载荷被另一对象种类解释。普通文本字段先严格解码 UTF-8,拒绝非法字节和孤立代理项;去掉文本开头的 BOM,把 CRLF 与单独 CR 变成 LF,再做 NFC。保留其他空白、行末空格、大小写与末尾换行,不做 NFKC 或语义修订。
- 结构对象递归使用合约声明的文本规则,再输出既有规范 JSON 的 UTF-8 字节;键顺序使用规范编码顺序,不依赖语言地区排序。重复键、归一化后碰撞的键或目录名直接拒收;非有限数、未声明字段及不符合 Schema 的输入拒收。只有合约声明为集合的数组排序,段落、步骤、文件块等有序数组不改顺序。
- 原文及逐字引文是保真字节,不做上述文本改写。 原文字节与引文以规范 base64 的字节对象引用进入同步结构;原文仍另存原始字节摘要,片段在该原件上核对。带 CRLF、BOM 或分解形式字符的原文不能为了同步而被悄悄改成另一份证据;普通 Markdown 记忆等可编辑文本才采用 LF/NFC。
- 因此“同步对象 ID”与“原始来源摘要”是两个有明确类型的字段,不能相互替代。客户端必须上传规范对象字节,Hub 重算并比较;不一边接受非规范字节一边猜测客户端哈希。先返回规范形式/摘要拒收,客户端用共享样例修正后重试。
- 对象内容摘要不含服务端接收时间、令牌、设备显示名或 Hub 核验结果;这些进入旁边的观察/回执。客户端编辑的父修订、对象 ID 与目录关系属于对象内容,必须入摘要。
第 3 条是为同时满足 B02 原文保真与本任务规范形式提出的明确边界,需在本次协议审查确认,不把“所有原文字节也统一换行”当作安全的默认实现。
8. 同步方式、清除与恢复边界
方式拟登记 local/hub,界面标签分别为“仅保存在本机”/“同步到 Hub”。本人选择方式,管理员无代改入口;团队页成员详情只显示方式和上次成功同步,不因此看到个人对象、冲突正文、目录、设备秘密或同步明细。个人页可展示本人同步结果,内部状态标识不进入界面。旧账户初始上次同步时间为未知,显示“—”,不能把控制台写入时间冒充成功同步。
建议迁移时,有既有个人知识/记忆/目录或其授权的账户保持 Hub 保存,标为 hub,避免无提示清空;其他空账户(含新账户)默认 local。local 下 Hub 不再接受个人知识/记忆的同步、控制台或 MCP 写入,也不允许新增它们的目录授权;仍可使用团队内容、提交独立团队操作。现有个人包的发布/安装/授权不属于本次知识与记忆清除,不借开关删除其独立注册记录;其依赖私人内容时照常重查可读性。既有测试夹具需要通过本人选择方式的测试入口显式启用 Hub,不能把旧业务期待批量改成拒收。
local → hub 建立新代次,初次上传仍走完整核验与容量检查,不自动重新打开旧授权。hub → local 按以下顺序进行:
- 本人查询影响;提交时带预检的期望状态与清除确认。数据已变化则拒绝并返回新影响,不替本人删除新增资料。
- 在本人空间锁内变更方式与代次,追加清除意图,立即拒绝旧代次的上传、提交、下载和所有授权读取;所有旧客户端、控制台、MCP、历史
tree:/src:/rec:、原始提交与缓存读取均须经过同一代次守卫,不只是隐藏当前列表。 - 个人知识与个人记忆的目录授权失效;旧授权记录保留作摘要审计,重新启用后不能重放旧授权。相关共享按来源关系追加失效/撤回状态,不能继续通过旧共享回执或依据读取已清除的私人内容;独立团队副本的边界见第 12 节。
- 清除该代次个人内容的可读载荷、全部历史候选、暂存对象、解析/搜索/记忆投影和进程缓存,释放个人逻辑容量。操作不是停用记忆或建立空目录快照;当前服务器不得仍以旧记录正文或对象路径返还被清除内容。
- 清除任务可崩溃恢复;事务提交后才执行不可逆的载荷清理,恢复时只能继续清理,不能自动退回旧代次。未完成返回 202 和“等待清除中”,完成后才给出完成回执与上次同步状态。重复同一切换操作不再清除新代次的数据,清理失败如实保留未完成状态。
本机副本不受影响,Hub 不发送删除本机文件的命令。旧客户端恢复联网先看到方式/代次变化,不能把清除前的本机快照自动传回;本人重新选择 hub 后才能初始化新代次。已发送给客户端的字节无法追回,进行中的分批响应在失效后停止交付后续批次。
载荷存储建议:新增按账户/代次隔离、可清除的个人载荷存储,由 records 提供专用端口。知识正文、目录名称、原文、引文、记忆、同步清单副本及客户端私有元数据均放在该域;审计记录只保存内容摘要、操作者、时间、客户端标识、数量和结果码,不能把正文再复制进拒收说明。载荷采用代次密钥加密并有原子落盘/回收,密钥不进入审计或同步协议。清除时停止访问并移除该域的密钥和载荷,不删除别人的、团队独立持有的或普通运行独立持有的同字节对象。
不能直接复用全局永不删除的 FileObjectStore 保存可清除私人正文,也不能只依赖移走“当前版本”。当前旧条目、目录快照及记忆在不可变记录中内联正文,必须解决旧载荷迁移,详见第 12 节;否则不能验收“记录只留哈希”。新增私有域同时纳入备份/恢复/权限测试,不能默默从备份漏掉仍在同步的个人资料;历史备份和已经独立成立的团队副本不承诺远程擦除,界面写明实际边界。
9. 容量、限流与错误
个人总额继续默认每人 1 GiB,沿用管理员容量设置。已提交对象、所有历史/冲突候选、暂存对象与记忆共用一份账户计量;同代次内同一对象只登记一次,暂存转提交只转用途,不重复收费。原文按实际字节计,不按 base64 膨胀后的 HTTP 长度计;快照页、目录和清单元数据也计量。旧记录已有历史用量须保留并对迁移结果核对,不能借导出/导入重置已使用额度。
暂拟默认限制如下,协议状态接口返回部署实际值:
| 项目 | 建议默认值 |
|---|---|
| 单个规范结构对象 | 8 MiB,另受现有条目、记忆和记录结构上限约束 |
| 原文字节块 | 1 MiB;整件原文仍受现有上传上限约束 |
| 每批对象数/解码字节 | 64 个/8 MiB,先达到者停止 |
| 每页清单 | 至多 500 项,并受正文预算约束 |
| 同账户请求频率 | 每分钟 120 次,读写共计;至多 2 个同步写请求同时核验 |
| 续传会话期限 | 24 小时;未提交对象仍占个人容量 |
HTTP wire 大小另外按 base64 膨胀与信封开销计算,且不超过服务器现有 maxRequestBytes 和全局在途预算;不能只约束解码后的大小。限制较小的部署协商更小对象/批次,不能返回无法实际接收的上限。容量预留、接收与提交都持同一个人空间锁,两个客户端不能各自通过后合计越限。
拟新增且同时登记到 hub/platform/environment.ts 的变量:CIRCLE_HUB_SYNC_MAX_OBJECT_BYTES、CIRCLE_HUB_SYNC_MAX_BATCH_BYTES、CIRCLE_HUB_SYNC_MAX_BATCH_OBJECTS、CIRCLE_HUB_SYNC_REQUESTS_PER_MINUTE、CIRCLE_HUB_SYNC_SESSION_TTL_SECONDS。其他大小由上述值和已有上限推导,不为每个内部常量增加配置。所有上下界与相互关系在 config 中检查,不使用退役的环境变量名。
复用 SOURCE_UNAVAILABLE、IDEMPOTENCY_MISMATCH、个人容量、凭据及记忆已有拒收码。拟登记 SYNC_VERSION_CONFLICT、SYNC_OBJECT_HASH_MISMATCH、SYNC_OBJECT_INVALID、SYNC_OBJECT_MISSING、SYNC_BATCH_LIMIT、SYNC_SESSION_EXPIRED、SYNC_MODE_LOCAL、SYNC_EPOCH_CHANGED、SYNC_RATE_LIMITED;最终以合约负责人审阅的代码为准,不在代码里先手写临时枚举。HTTP 分别使用 409(状态/代次竞争)、413(请求/批量大小)、422(本人对象结构/身份问题)、429(限流并带重试时间);404 不能细分“他人拥有”与“没有对象”。
10. 合约、审计、迁移与界面
- 模型在当前 24 基础上顺延,覆盖 K01 的个人同步/冲突/清除边界、B02 的原文保真、R02 的本人同步权限及 D06 的六工具不变。新增
sync_mode、同步进度/冲突的显示轴及对象/操作封闭类型,全部由生成物提供;“同步方式”“有冲突”“上次同步”“等待同步中”“等待清除中”等标签经模型生成,不能在控制台另造词。内部 CAS、摘要和代次只出现在协议与开发文档。 - 拟新增同步方式变更、提交、传输回执与清除记录,动作复用已登记的写入/校验种类;如需新种类在审查时登记,不假借真实业务读取。每次同步记录本人、服务器确认的客户端与声明设备、起止时间、上传/下载/已存在对象数及字节、旧/新状态、结果和拒收码。审计不含对象正文、标题、路径、引文或自由客户端说明。本人看自己的明细,管理员只经已批准的成员详情看方式/时间,不获个人同步内容的隐含读权。
- 新迁移从当前 13 后追加,保存代次控制、快照头、本人对象拥有/用途、暂存会话/幂等回执、冲突和容量投影;不可变证据记录与可更新投影分开。新锁使用命名的双键命名空间,优先复用现有本人空间锁,不在持锁前后反向取得团队锁。
- 每个新运行表同步
RUNTIME_GRANTS与 ownership 的COLUMN,更新重建/旧库升级测试。旧迁移 SQL 逐字不动,复核新增 SQL 后才更新migrations.snapshot.test.ts的整源摘要;密码测试继续取MIGRATIONS.slice(7),不钉迁移数。 - 通过生成器同步必需协议文件;依前期同类授权,只同步四个通用技能的协议镜像/清单及必要补丁版本,不改业务代码,测试从
repositorySkillVersion取版本。HTTP 同步 Schema 与模型工具 Schema 分开,不把路由加入通用技能给模型的使用说明。 - 页面只接入方式设置、本人冲突事实、成员详情两个字段及清除结果,保持现有布局;新的控制台设计系统不在本任务实现。README/CHANGELOG/决定 138 在真实实现后同步更新,说明实际完成与 Circle 客户端尚未实现的边界。
11. 共享样例、实施提交与验证
共享样例拟放 contracts/test-fixtures/sync/v1/,以纯 JSON、UTF-8 文本及明确的 base64 字节表示,附 Schema/编码版本和确定的预期字节、对象 ID、缺失清单、合并结果、拒收码。夹具无时钟、随机值、数据库 ID 或客户端私有配置依赖;稳定 ID、设备和时间均为合成常量。两边跑同一份数据,不让 Circle 依赖 Hub 数据库、服务器模块或业务代码。
样例至少覆盖:LF/CRLF/CR、头部 BOM、组合与分解 Unicode、中文/emoji、内部 BOM 与尾随空白保留;键顺序、重复键及 NFC 碰撞拒收;带 BOM/CRLF 的原文和引文保真;跨块原文、空文件、摘要不符;两台客户端各自修改不同对象、同项竞争、删除对修改、目录移动、冲突选择;CAS 失败与重试、重复批次、丢应答与续传;原文缺失/错引文的未核验;记忆冲突、篇数/上下文字节限制及编码凭据拒收。另以服务测试覆盖他人同字节对象、跨账户/代次访问、停用/令牌撤销、暂存耗尽容量和切换后旧端重放,不把完整性样例当成权限证明。
审查通过后拟分以下提交,每步带受影响检查,完成一阶段先报告:
- 合约、规范编码与共享样例;保持六工具数量与原文引用字节。
- 个人载荷存储、旧库兼容和清除边界;先证明历史正文不能被旧入口读回,再开放同步 API。
- 版本头、稳定 ID、批量接收/下载、续传与按账户计量;追加迁移和运行角色约束。
- 原子 CAS、确定性冲突与重新核验;联通现有控制台/MCP 写入与代次守卫。
- HTTP 同步、方式切换/清除、最低限度页面字段及安全审计。
- 定向验收、共享样例冻结、文档与决定 138;报告 Hub 完成范围,不启动 Circle 端任务。
每步执行 npm run typecheck 及本步受影响文件,并按任务要求包含以下四项;ownership 使用 PostgreSQL 18,重套件顺序执行,必要时 HUB_UNIT_CONCURRENCY=4:
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" node --test --test-concurrency=1 \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts \
hub/platform/ownership.test.ts hub/records/lock-namespaces.test.ts
合约变化运行 npm run gen:contracts、npm run verify:contracts 和对应模型/共享样例测试;迁移运行快照、密码、运行角色、旧库升级、ownership;私有数据新增存储位置时补备份/恢复的定向用例。路由覆盖 Bearer/OAuth、Host/Origin、拒读先于正文解析、限流、撤销和日志;新增 tests/acceptance/49-personal-sync.test.ts,并运行受影响的 46/47/48。改生产运行或依据读取时,按前项持续要求执行全部 packs/workflow/**、hub/runs/engine/** 测试及受影响验收,实际运行 macOS 沙箱项,逐项列出跳过。
收尾运行 npm run test:docs 与差异检查。只做任务要求的同步对象身份核对及明确要求的迁移源复核;不加无关 SHA 验真,不把这些检查扩大为全量 unit/acceptance。来源归档工具未改则不运行来源检查。每次记录真实命令、计数、首次失败与修正、跳过/未运行项。
12. 本次请 Claude 审查的四个具体边界
- 已有内联正文怎么清除:建议 records 增加私有载荷的存储间接层,在不改变原文、逻辑记录摘要与引用的前提下,把既有个人正文迁入可清除域;之后只通过原引用重建可读正文。现有
records.body内联保存、数据库禁止更新记录,全局原件又永不删除,因此旧数据搬迁涉及受限的物理存储布局转换,不能仅靠新增一条空快照做到。需明确认可这项只针对个人载荷的转换范围、如何保留旧记录摘要及不可读证明,并允许定向升级/恢复测试;否则“旧正文也清除”和“旧物理记录字节完全不动”不能同时承诺。审查前不改记录、不弱化通用防篡改触发器、不把接口过滤冒充擦除。 - 备份与独立副本:建议清除当前服务内该个人代次的全部载荷并使其目录授权/依赖共享失效;仍由该个人共享控制的团队条目追加撤回,未被管理员改动的团队记忆共享副本停用。管理员后来独立改写的团队副本、其他账户或普通运行独立合法持有的原文,以及离线旧备份,不因同字节而被破坏,也不承诺远程擦除;这些副本不能再借已经失效的私人来源读权访问旧正文。请确认这个“相关分享失效”范围,避免实现时擅自删团队已独立维护的资料。
- 规范形式的保真例外:建议普通可编辑文本统一 LF/去头部 BOM/NFC,证据原文与逐字引文用保真字节对象承载,保留原始摘要;对象封套仍按规范形式寻址。不对旧原文做就地改写或重签,这与 B02 的原样保存要求一致。
- 初始同步方式:建议既有有数据账户保留
hub,新的空账户默认local,本人明确选择后才在 Hub 写个人正文;这会要求旧测试夹具显式选择方式,但不改变其业务结论。若默认方式有其他产品要求,应在此审查确定后再生成词表和状态迁移,不事后用隐藏默认值兼容两种含义。
13. Claude 审查结论(2026-11-02)
方案通过,按第 11 节的六步实施,每步完成后报告。以下决定回答第 12 节的四个问题,并调整其余几处。
- 旧内联正文:同意增加个人载荷的间接存储,并对既有个人正文做一次性的物理布局转换。条件:
- 用新增迁移完成,旧迁移不动。转换只针对个人空间的知识正文、文件夹名称、原文、引文与记忆;逐行核对记录摘要在转换前后不变。
- 防篡改触发器保留。放行只在迁移所有者角色内生效,只针对上述行,只允许把正文换成“摘要+载荷引用”的形态。运行角色无法使用这项放行,用 ownership 与运行角色测试证明。
- 完整性检查对已转换的行改为:有载荷时核对载荷;已清除时只核对摘要链并标记为已清除。为此补测试。
- 新写入从一开始就进入可清除存储,不再内联。
- 不做按代次加密。可清除存储按账户与代次隔离,物理删除即可;同字节只在同一账户、同一代次内去重。
- 目前唯一的部署(所有者本机)停在个人空间上线之前,没有个人正文;转换仍要有旧库升级测试覆盖。
- 备份与独立副本:分享到团队产生的团队知识与团队记忆属于团队,清除时不撤回、不停用。
- 团队副本里仍依赖已清除个人来源的部分,按“来源无法打开”处理:变为待验证,管理员可见。
- 随清除失效的是三项:本人文件夹对成员和管理员的分享(从“与我共享”中消失);团队副本指回个人原件的关联;尚未完成的分享到团队检查。
- 离线备份不远程擦除。恢复清除之前的备份会带回已清除的内容,恢复命令要给出提示,文档写明。
- 规范形式:同意。可编辑文本统一为 LF、去掉头部 BOM、NFC;原文与逐字引文按原样字节保存,不改写;对象封套按规范形式寻址。
- 初始同步方式:所有现有账户和新账户默认“同步到 Hub”,与第二、三阶段的行为一致,个人空间开箱即用。
- “仅保存在本机”由本人明确选择,走预检加清除流程。
- 选择仅保存在本机后,控制台“我的”显示同步方式和切换入口,不显示空白页。
- 这一项是协调人的决定,会汇报给所有者,所有者可以改。
其他调整:
- 配置变量只加两个:
CIRCLE_HUB_SYNC_MAX_BATCH_BYTES、CIRCLE_HUB_SYNC_REQUESTS_PER_MINUTE。对象大小、批量对象数和续传期限用常量,或由现有上限推导。 - 控制台切换同步方式走控制台自己的路由,沿用现有的会话与防跨站检查,调用同一个服务函数。同步 HTTP 接口仍只认 Bearer 令牌。
knowledge_entry_status.unverified的界面用语是“待验证”。词表里的旧标签由控制台用词任务统一修改;本任务引用生成的标签,不新造。- 新增的界面用语用主流说法:同步方式、仅保存在本机、同步到 Hub、上次同步、有冲突、等待同步中、等待清除中。
发现
本节保留方案阶段的发现;六步实施及实际验证见后续逐步记录,决定 138 已在进度页正式记录。
- 当前知识版本是单父链,记忆也是单篇单父修订;尚无同步稳定 ID、代次、对象拥有清单或跨知识/记忆的原子同步头。需要新协议和适配层,不能直接把原记录摘要当作可编辑对象 ID。
hub/records/store.ts的不可变记录内联正文,hub/records/objects.ts的全局原件永不删除;spaces.ts与memory.ts的历史同样保存私人内容。严格清除与这些旧存储约束需要在方案审查时解决,不能只加读取过滤。- 现有规范 JSON 不负责 LF/BOM/NFC;原文与逐字引文身份也不能静默规范化。本方案把新同步编码与旧证据身份分开,明确交审查。
- 决定 138 尚未正式记入进度页;本步是依据决定 131、135 的待审方案,不提前记录实现完成。
做了的
- 第 1 步:阅读任务页、现行设计的 R7、决定 131/88、模型 K01/K02/B02/R02/D06,以及知识/记忆、认证、记录/原件、容量与备份的现有实现;补齐以上待审实施方案。
- 本步仅修改本任务页,待 Claude 审查后再实现。没有新增接口、合约、配置、迁移或 Circle 客户端代码,没有部署、推送或创建 PR。
验证
第 1 步仅改方案文档,本机 Node 24.22.1,检查如下:
npm run typecheck:通过。- 上文列出的环境变量、缺省知识端口诊断、PG 18 ownership、锁命名空间四个文件:17 项通过、0 失败、0 跳过,ownership 的 3 项实际运行;使用
CIRCLE_HUB_LIBPQ_BIN指向已获准的 PostgreSQL 18 客户端。 npm run test:docs:290 项通过、0 失败、0 跳过。git diff --check:通过;仅本任务页有变更。
计划中的新协议/权限/同步验收用例尚未实现,未运行,不算通过。本步未改合约或迁移,未运行生成/合约核对、迁移升级、46/47/48/49、备份恢复、完整工作流/引擎或全量 unit/acceptance、Linux CI、来源归档检查;均不计作通过。实际运行的检查没有跳过。没有读取个人环境配置和凭据库,没有操作持久部署或其他工作树。
实施第 1 步:合约、规范编码与共享样例
来源:Claude 2026-11-02 批准本页第 13 节,要求按六步实施并逐步报告。
- 模型升至 25,登记同步方式、状态、对象种类和同步信息字段,以及九个同步拒收码;不修改待验证的现有显示标签。默认方式明确为同步到 Hub;原文保真、无代次加密、团队副本保留写入模型。没有新增 MCP 工具、配置变量或数据库迁移。
- 独立 HTTP 请求/答复、12 个路由、严格对象封套、CAS、清除确认、分页、续传回执和核验观察写进 contracts/src/sync.ts,生成独立 Schema。HTTP 协议没有进入工具输入/说明;tools.json 无变化。
- 可移植规范编码只依赖合约、zod 与 Node 内置模块,不依赖 Hub。普通文本 LF/去头部 BOM/NFC,严格 UTF-8/JSON,拒绝重复键、归一化碰撞、非法 Unicode、非规范 base64 和身份不符;原件/引文按保真字节。单块有界、父/头集合严格排序去重,不悄悄补默认内容改变对象身份。
- contracts/test-fixtures/sync/v1 保存独立预期的六组编码字节/摘要、原文/引文/知识/记忆对象、合并与续传情景及 HTTP 清单。当前测试证明编码和夹具一致性;实际 CAS、冲突、权限、容量、清除需要后续服务/验收验证,没有把声明样例当作服务已实现。
- 依已批准的生成同步范围,仅更新四个通用技能的协议镜像、补丁清单,不改业务代码,测试继续使用 repositorySkillVersion。
- 初次生成引用了不存在的基础类型,改为既有 PackVersion 后通过;首轮定向 55 项中 54 通过、1 个固定模型版本期待遗漏,修正为 25 后扩大合约组 413 项通过。最后补上块大小与缺省字段回归后的结果续记。两次文档编辑脚本解析失败未改变文件,随后使用 Node 编辑;失败不算验证通过。
本步只完成合约与样例,HTTP 路由、数据存储和 Circle 客户端均未实现;第 2 步尚未开始。
第 1 步最终验证
npm run typecheck通过;npm run gen:contracts与npm run verify:contracts通过,175 个生成文件一致,生成物未手改。- 下列最终合约/包约束/指定基线组 416 项通过、0 失败、0 跳过;包含新增编码和 HTTP 隔离回归、四技能清单验证及 PG 18 ownership 的 3 项。先前 55 项组的版本期待失败已修正,不将其算作整组通过;中间扩大组 413 项通过,最后新增回归后再执行以下最终组。
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" node --test --test-concurrency=1 --test-reporter=tap \
'contracts/src/*.test.ts' \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts \
hub/platform/ownership.test.ts hub/records/lock-namespaces.test.ts \
packs/workflow/case-review/packs.test.ts
npm run test:docs290 项通过、0 失败、0 跳过;git diff --check通过。四技能版本为 html-format/mindmap-format 1.0.12,xml-format/workbook-format 1.0.13。- 同步处理器、实时 CAS/冲突/权限/清除、迁移/备份恢复及验收 49 未实现,未运行,不计通过;没有运行 46/47/48、全工作流/引擎、全量 unit/acceptance、Linux CI 或来源归档检查。没有改生产运行/依据、迁移或来源工具。没有新增与本次同步无关的数据 SHA 校验。
- 仅本机签署第 1 步提交;没有推送、PR、部署或操作 Circle 仓库。按要求先报告第 1 步结果,再进入可清除载荷的第 2 步。
实施第 2 步:可清除载荷、旧正文转换与清除边界
依据:Claude 2026-11-02 明确批准第 1 步,并要求遵守第 13 节的受限转换、摘要不变和无代次加密条件。
- 新增迁移 14;迁移 1~13 的 SQL 不改。个人记录从首次写入就保存为摘要胶囊,原正文及目录/原件字节进按账户、代次隔离的 PostgreSQL bytea 载荷。没有代次加密、密钥或新的配置变量;同字节不同账户物理分存。审计胶囊和清除/续办日志只保存摘要与安全标识。
- 一次性转换仅针对个人空间的知识、目录快照、原件、引文、目录授权与记忆。迁移所有者逐行验证原逻辑摘要,正文转成载荷引用后保持原摘要、ID、时间等元数据不变。防篡改触发器继续启用,仅在迁移 14 尚未登记、调用者恰为表所有者、只改正文且胶囊/载荷完全匹配时放行;完成后不能重用。额外的延迟约束拒绝伪造绑定来遮蔽团队记录。
- 旧目录树/原件在监听前分批物化;持久的摘要续办日志保证中断后可继续收尾。删除原全局文件前保护独立团队树的所有下级节点以及独立原件采集;不会因为根不同而误删相同子节点。全局原件机制保持,新的私人对象不落入不可清除的全局文件。
- 清除服务只接受当前身份自己的代次,持既有个人空间锁;物理删除该代次载荷与搜索/记忆投影,原记录胶囊留存。当前和历史记录、原件、知识与记忆的目录授权均不可继续读取;新写入拒收。载荷仍在时核对原字节与逻辑摘要,清除后核对胶囊链并标记已清除;缺失或篡改的活动载荷按完整性失败处理,不冒充成功清除。
- 团队知识与团队记忆不删除、不停用。直接或间接仍需已清除个人依据的旧团队条目在读取时显示待验证,历史正文不改。当前共享本来就要求先有可供团队读取的前件;回归保留该拒收规则,另以显式历史夹具覆盖旧依赖,而不是放宽发布规则来造夹具。
- 备份沿用同一 PostgreSQL 一致快照,包含载荷与胶囊;ownership 增加真实 PG 18 的“备份→清除→恢复旧备份→再次清除”断言。恢复命令说明旧备份会带回已清除内容,不追溯擦除旧备份。
- 首轮新增清除检查复现 NUL 的 PostgreSQL 元数据解码失败,修正仅用于检查的元数据视图,正文和摘要不改;旧库角色用例首次未给临时账户配口令而认证失败,补合成口令后通过。扩大组 62 项中 61 通过、1 项因行锁要求 UPDATE 权限失败,改为复用既有业务锁,不扩大运行角色权限。团队依赖夹具两次先后因来源 ID 不符合语法、当前共享资格不足而失败,修正来源 ID 并保留当前拒收断言,采用明确的旧团队记录夹具后通过。
本步没有创建同步 HTTP、模式切换控制台或 Circle 客户端;先报告第 2 步,不开始第 3 步。
第 2 步最终验证
- 本机 Node 24.22.1;数据库均为临时 PostgreSQL,ownership 使用已批准的 PG 18 客户端;合成原件和临时数据目录均随测试清理。没有读取个人配置、凭据库或持久部署。
- 最终增加了运行角色即使临时获得正文 UPDATE 权限、设置迁移标志并伪造临时系统目录仍被触发器拒绝的回归;迁移所有者也不能把团队记录转换成个人记录。旧转换与原件清理未完成时拒绝清除;模拟“私有字节已复制、旧文件尚未删除”的中断点,重开后完成清理。私人原件的单独采集和空间事务统一先拿空间锁,再拿采集锁;12 路并发仍得到同一采集身份。
- 转换逐行核对原记录摘要,升级测试同时核对迁移 1~13 的登记摘要不变;仅追加迁移 14。复核新增 SQL 后,迁移源文件固定摘要更新为
48b5c333b10a96e5cec1798cc8bd3ecf0e4cfb51c5d6960ebc1dcdf024510bb6;没有更新知识视图输出的固定摘要。 - 首次扩大记录/知识/验收组 324 项中 323 通过、1 项失败:通用防篡改触发器对其他表求值了 records 专用字段。改为先检查表和操作,再检查转换条件,保留全部只追加拒收断言;随后复查组 48 项通过。最终以下记录/平台组 245 项通过、0 失败、0 跳过,包括所有记录测试、密码迁移、运行角色、锁命名空间、PG 18 ownership 的 3 项、CLI 恢复提示与引用次数夹具。
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 \
node --test --test-concurrency=1 --test-reporter=tap \
'hub/records/**/*.test.ts' hub/knowledge/personal-purge.test.ts \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts \
hub/platform/ownership.test.ts hub/platform/cli.test.ts hub/runs/engine/citations.test.ts
- 最后补充单条/批量正文、业务键、记录 ID、索引查询和历史列表等旧读取入口在清除后均无法读回的断言,单独复跑
hub/records/personal-payloads.test.ts:5 项通过、0 失败、0 跳过。 - 最终知识服务、包约束与受影响验收组 87 项通过、0 失败、0 跳过。以上检查共同覆盖知识与记忆的未完成共享检查失效、成员/管理员旧授权失效、团队记忆保留、依赖个人依据的历史团队知识显示待验证、旧备份恢复内容及新的私人写入仍拒收。最后新增的间接依赖夹具首次因未按内容生成条目 ID 而被拒读,修正夹具后重跑本组,保留内容身份校验。知识视图输出字节保持既有值,冷 49,000 条用例仍在现有限额内;没有为通过测试更改输出摘要。
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 \
node --test --test-concurrency=1 --test-reporter=tap \
hub/knowledge/personal-purge.test.ts \
hub/knowledge/memory.test.ts hub/knowledge/spaces.test.ts \
hub/knowledge/sharing.test.ts hub/knowledge/view-perf.test.ts \
packs/workflow/case-review/packs.test.ts \
tests/acceptance/46-knowledge-spaces.test.ts \
tests/acceptance/47-knowledge-sharing.test.ts \
tests/acceptance/48-knowledge-console-memory.test.ts
- 按依据读取变更的持续要求,顺序运行全部工作流与引擎组:2,765 项中 2,764 通过、1 失败、0 跳过。唯一失败是 citations 的合成个人空间夹具使用不存在的账户,违反新增外键;修正为先创建合成成员,保留原计数和隐私断言。该文件最终在上文 245 项组中 5 项全部通过。没有把首次整组失败记成通过;没有重复整个 17 分钟的组来代替针对夹具的复查。macOS 沙箱依赖项实际执行,没有因缺少沙箱跳过。
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 \
node --test --test-concurrency=1 --test-reporter=tap \
'packs/workflow/**/*.test.ts' 'hub/runs/engine/**/*.test.ts'
npm run typecheck通过;npm run verify:contracts通过,175 个生成文件一致,本步没有改合约或技能镜像;npm run test:docs290 项通过、0 失败、0 跳过;git diff --check通过。- HTTP 同步、版本头/CAS/冲突、同步容量计量与验收 49 尚未实现,未运行,不计通过。未运行全量 unit/acceptance、Linux CI、来源归档检查;本步未改来源工具,不添加无关 SHA 数据核对。所有实际运行的测试均无跳过;首次失败及修正如上,不将跳过或未运行项算作通过。
- 本步只在 kw-sync-local 签署本机提交,没有推送、PR 或部署;按要求报告第 2 步后再进入第 3 步。
实施第 3 步:版本头、稳定 ID 与续传存储
来源:Claude 2026-11-03 审阅 e2d6c98c 后要求先实施第 3 步,独立安全审查的发现须在第 4 步之前单独修复。本步不修改迁移 14;新增迁移 15。
- 新增 records 的内部 personalSync 端口,接入 RecordsKit 和知识上下文;还没有 HTTP 路由、MCP 工具、知识/记忆投影或包发布入口。上传只进入本人暂存域,不能被原件引用、搜索或记忆自动加载读取。
- 版本头和原生身份到 UUID 的稳定映射持久化;身份键只存摘要,不存目录名称。状态身份包含本代次、方式、同步快照及原生知识/记忆头,不把上传、心跳或续传时间算成内容变化。原生内容导出、包清单映射和原子推进由第 4 步联通,不把当前内部空头说成已有内容已同步。
- 客户端名称存可清除的本人载荷;账户、代次、会话、对象和操作全部限定到同一作用域。每次操作在既有个人空间锁内复查账户与方式,停用账户、旧代次和清除后的旧会话停止传输。运行角色不能直接改 personal_spaces,不能直接删除载荷或传输目录。
- 会话用索引行冻结已提交快照的完整对象图,不把一次 500 项分页当作全量清单;509 个对象的回归覆盖分页、后来上传及当前头变化。上传和 missing 命中的本人对象只延长该会话的暂存引用,不改变冻结清单;游标绑定账户、代次、会话和冻结快照。
- 整批先检查规范字节、身份、类型、大小、凭据,再原子保存。按解码后的字段扫描记忆/结构文字,字节块按原样扫描;未能检查的二进制仍只是本人暂存,不声明其可作模型依据。完整原件的拼接、跨块扫描和重新核验属于第 4 步提交检查。
- 同操作键绑定路由、会话及请求摘要;原回执跨重启可重取,同键异请求拒收。回执不含正文、标题、路径或客户端说明。注入回执写入失败,证明同批载荷和对象登记一起回滚,不留部分成功;上传不推进当前快照或上次同步。
- 保留既有知识与记忆历史用量,叠加本账户本代次的唯一同步对象与客户端元数据;原文字节块计原始字节加封套元数据,不计 base64 膨胀。同对象多次接收只计一次,并发两批不能共同越过容量上限。第 4 步将这一计量联通所有原生写入和同步投影,避免投影重复计费。
- 新增所有者定义的 hub_collect_personal_sync,仅在最后一个活会话结束/过期且无当前头/保留历史对象图引用时回收暂存载荷。保留快照会保护其全部后代,而不是仅保留根;独立原件和个人记录载荷不误删。清除仍删除该代次全部载荷,安全摘要与回执保留。真实 PG 18 备份恢复增加暂存对象、会话及操作回执。
- 按第 13 节只新增 CIRCLE_HUB_SYNC_MAX_BATCH_BYTES,默认 8 MiB,并登记平台转发清单;对象数、分页和 24 小时期限取已批准常量,实际批量/对象大小还受 HTTP 请求预算限制。请求频率变量留第 5 步。
- 已记下 Claude 对第 5 步的要求:local → hub 用所有者定义的方式切换函数建立新代次,不给运行角色增加 personal_spaces UPDATE 权限;本步不提前开放方式切换。
- 首轮新增组 9 项中 7 通过、2 失败:历史记忆计量夹具缺少被外键引用的记录,凭据夹具只有字母、不符合既有检测规则;分别补齐合成记录和符合检测规则的合成值。记忆/结构扫描同时明确按解码字段执行,沿用现有记忆扫描语义。补验收后 26 项通过;增加已应用迁移 14 的升级、保留快照完整图及较小请求预算等回归后,新增文件 11 项通过。一次编辑脚本方法名写错未更改文件,修正后继续。
本步不开始第 4 步。
第 3 步最终验证
- 本机 Node 24.22.1;所有账户、原件和设备说明均为合成数据,使用临时 PostgreSQL/钥匙串和测试 Hub。没有读取个人环境配置、凭据库或操作持久部署。
- 追加迁移 15 后,迁移 1~14 的定义与 e2d6c98c 逐字一致;13 → 当前、已应用 14 → 15 和密码迁移升级均有真实数据库回归。复核新增 SQL 后,迁移整源固定摘要为
e2965cff407a0bb35cf2bb5b43475dcb23cade2faa2332099a47e17069ffd9bf。没有新增无关 SHA 数据核对。 - 首轮新增 9 项中 7 通过、2 失败;随后带配置组的 25 项中 24 通过、1 个凭据夹具仍失败;补上合成值中的数字后,含新增验收的 26 项全部通过。最终新增文件增加到 11 项并全部通过,0 跳过。
- 以下定向组 329 项中 327 通过、2 失败、0 跳过。两项失败都在 ownership:探针误选了同步头获准更新的 snapshot 列;改为检查不可更新的 owner 列,权限不变。没有放宽测试期待,没有把初跑整组记为通过。
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 \
node --test --test-concurrency=1 --test-reporter=tap \
'hub/records/**/*.test.ts' 'hub/config/*.test.ts' \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts \
hub/knowledge/personal-purge.test.ts hub/knowledge/memory.test.ts \
hub/knowledge/spaces.test.ts hub/knowledge/sharing.test.ts \
packs/workflow/case-review/packs.test.ts \
tests/acceptance/46-knowledge-spaces.test.ts tests/acceptance/47-knowledge-sharing.test.ts \
tests/acceptance/48-knowledge-console-memory.test.ts tests/acceptance/49-personal-sync.test.ts
- ownership 探针修正后,以下最终复查组 36 项通过、0 失败、0 跳过。ownership 的 3 项都使用 PostgreSQL 18 客户端实际运行,暂存载荷、续传会话及幂等回执均从旧备份恢复成功;不是跳过后算通过。未重复已通过的其余定向文件。
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 \
node --test --test-concurrency=1 --test-reporter=tap \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts \
hub/records/lock-namespaces.test.ts hub/records/migrations.snapshot.test.ts \
hub/records/password.test.ts hub/records/roles.test.ts \
hub/records/personal-upgrade.test.ts hub/records/personal-sync.test.ts
npm run typecheck通过;npm run verify:contracts通过,175 个生成文件一致,本步未改模型、协议 Schema 或技能镜像;npm run test:docs291 项通过、0 失败、0 跳过;git diff --check通过。- 验收 46/47/48/49 均通过;49 本步证明真实 Hub 的内部端口、两个客户端、六工具边界、暂存不发布和清除,不冒称 HTTP/CAS 已完成。48 需要的 macOS 沙箱实际运行,0 跳过。
- CAS/冲突、原生内容映射、完整原件/引文重新核验、同步 HTTP、令牌撤销/限流和方式切换仍属后续步骤,相应验收尚未实现、未运行,不计通过。未运行全量 unit/acceptance、全部工作流/引擎、知识视图性能、Linux CI 或来源归档检查;本步未改生产运行、依据读取、知识视图或来源工具。
- 仅在 kw-sync-local 签署本机提交,无推送、PR 或部署。收尾时发现 Claude 新增的未暂存附录 kw-sync-review.md,已读到产品挂载升级、独立持有者与运行角色等审查意见;该文件保持原样,不并入本步提交。审查修复须在第 4 步之前另作提交。本次先报告第 3 步,不开始第 4 步。
第 2 步安全审查修正 1:产品挂载与升级启动
来源:Claude 2026-11-03 要求先处理 kw-sync-review.md 第 1 项。审查附录原样纳入本次修复提交;探针改写为仓库回归,不从临时审查目录导入。
- 先新增带/不带产品挂载的旧库升级用例:修复前 2 项中 1 通过、挂载用例因“团队目录原件缺失”失败,确认复现。修复在每个历史团队版本的保护遍历中跳过其 products[].root;目录对象仍保留,产品知识根由知识记录读取,不作为对象文件打开。
- 同时加入真实合成产品生产、挂载和待完成退役日志的重开回归;验证产品节点仍可读、团队目录文件保留、旧个人全局文件被退役,首次及再次启动成功。最初真实夹具漏填 build.root 且用记录摘要入口读取知识内容摘要,修正为 root:null 和知识节点端口;不改变产品生成规则。
- 本项不改迁移 14 或 15,也不改变清除权限。其余审查项尚待各自提交;第 4 步未开始。
验证:npm run typecheck、git diff –check 通过。以下定向组 22 项通过、0 失败、0 跳过;macOS 沙箱生产及 PG 18 ownership 的 3 项实际运行。首次复现的失败和真实夹具修正均如上记录,不算初跑通过。
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" node --test --test-concurrency=1 --test-reporter=tap \
hub/records/personal-mount-upgrade.test.ts hub/records/personal-upgrade.test.ts \
hub/knowledge/personal-mount-recovery.test.ts hub/platform/environment.test.ts \
hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts hub/records/lock-namespaces.test.ts
本项未运行全量回归、全部工作流/引擎、Linux CI 或来源检查;没有修改运行或依据读取逻辑。文档检查在本轮收尾执行。
第 2 步安全审查修正 2:独立持有者与采集范围
来源:kw-sync-review.md 第 2 项及 Claude 提供的合成探针。迁移 14 尚未部署,依明确许可修改该未部署迁移;迁移 1~13、15 的 SQL 不改。
- 在转换前建立精确字段引用索引 object_holds,并由所有者触发器维护。记录运行角色只有 SELECT,不能自行插入“持有者”。索引识别实际存储字段:原件 sha256、资源/变量/执行器授权 bound、包清单与文件、原始提交对象、冻结执行器映射、生产列表与缓存、已登记投喂对象及其原件记录。生成输入、任意文字和一般依据提及不算独立存储所有权。
- 非个人知识记录已持有的原件记录不转换;独立持有同字节对象时保留全局文件,只清除个人知识的载荷和关联。执行器快照映射的子对象同样保留。退役查询改为精确索引,不再对每个摘要全表做子串匹配;低风险第 3 项的否定回归另作提交。
- 个人知识采集的去重与原件记录均带受信任的账户/代次范围;普通采集身份不改。即使名字、字节、作者、时刻完全相同,也不会复用独立个人包的采集记录;清除知识不移除个人包原件。本项内部可选元数据不新增词表或记录种类。
- 改写审查探针为 9 类旧库持有者升级回归,并增加固定时钟的采集隔离回归。首轮 10 项中 9 通过,隔离夹具误把 importFile 返回的原件当成来源关联,修正为实际原件摘要后 10 项通过。一次 SQL 编辑脚本误用字符串替换元字符,检查差异发现后立即从本项起点重建该文件,未运行或提交错误迁移。
验证:npm run typecheck、git diff –check 通过;新增持有者/隔离组 10 项通过。下列受影响组 292 项通过、0 失败、0 跳过,包含全部 records、运行角色/升级/锁、PG 18 ownership、个人技能引用及验收 46~49;macOS 沙箱项实际运行。复核未部署 SQL 后,迁移源固定摘要更新为 ffb0e57ef8637a6fe98696e588a332de4e9bc7040831138d1a5336c48eed6644。
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
'hub/records/**/*.test.ts' hub/knowledge/personal-import-scope.test.ts hub/knowledge/personal-mount-recovery.test.ts \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts \
'hub/refs/*personal*.test.ts' packs/workflow/case-review/packs.test.ts \
tests/acceptance/46-knowledge-spaces.test.ts tests/acceptance/47-knowledge-sharing.test.ts \
tests/acceptance/48-knowledge-console-memory.test.ts tests/acceptance/49-personal-sync.test.ts
本项未运行全量 unit/acceptance、Linux CI 或来源检查;全部工作流/引擎及本轮文档检查将在涉及依据读取的修复完成后统一记录,未运行不算通过。
第 2 步安全审查修正 3:提及不等于持有
第 3 项与第 2 项共用精确持有字段的实现;本提交单独固化其否定回归。将审查的 h5-mention 探针改写进仓库,增加文件名和节点说明两种变体:清除后旧个人原件的全局文件实际不存在,对象入口返回不存在;被提及的摘要不会变成存储所有权。另一份真实独立原件及其记录保留。
验证:npm run typecheck、git diff –check 通过;以下组 29 项通过、0 失败、0 跳过,包含真正独立持有者的正向回归及 PG 18 ownership。没有从审查临时目录导入代码。本项不改迁移、运行或依据规则;未跑全量回归/Linux CI/来源检查,文档检查留本轮收尾。
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" node --test --test-concurrency=1 --test-reporter=tap \
hub/records/personal-mention-retirement.test.ts hub/records/personal-retirement-holds.test.ts \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts hub/records/lock-namespaces.test.ts
第 2 步安全审查修正 4:运行角色约束
- 原件绑定额外要求同一所有者、同一代次的知识来源关联;胶囊必须恰有规定的摘要/范围/证明字段。审查探针中把独立上传正文伪装成胶囊的 INSERT 现在在事务提交时被拒绝,原上传及全局文件保留。未关联、关联到其他所有者的私人原件也拒绝并回滚。
- personal_payloads 和 personal_objects 的插入前触发器由所有者执行,只接受当前 hub 代次并拒绝已清除代次。范围行的共享锁与清除串行,直接运行角色并发插入也不能在清除后留下新载荷;原件登记还必须已有同范围载荷。
- 运行角色不再直接 INSERT ready、retire、retired 三种完成日志。新增只读的旧转换候选清单;所有者函数核对真实载荷和完整目录图,原子加入旧文件待处理清单并标记完成,避免先标完成后漏登记旧文件。新私有记录由延迟约束完成同样检查。退役完成函数只接受已登记、转移已完成的对象;文件删除检查由应用执行,目录同步风险另在第 8 项修复。
- SQL 中先按载荷键取数据后解析目录;来源关联读取已还原记录,防止优化器在关联过滤前解析二进制。初次 11 项组因并发用例暴露该解析顺序问题而失败 1 项;修正后 11 项通过,扩大旧库持有者/提及回归组 23 项全部通过。旧直接私人采集夹具改为在同一事务写入其知识来源,保留原隔离和并发断言,没有放宽约束。
- 清除成员身份授权仍在应用层:入口从已认证主体推导 owner。数据库运行角色代表整个 Hub,并不代表单个成员;数据库函数接受应用传入的成员标识,不把 SQL 执行权限当成成员授权。这与现有写操作边界相同,未开放给 MCP 或同步 HTTP。
最终验证:npm run typecheck、git diff –check 通过;下列组 299 项通过、0 失败、0 跳过,PG 18 ownership 与 macOS 沙箱实际运行。复核未部署迁移 14 后,源文件固定摘要为 fa0a23f2d3619b78173bc93d539ffd26cae194d08bdfcfb565bfeb138dc823a1;迁移 1~13、15 的 SQL 不改。全量 unit/acceptance、Linux CI、来源检查未运行;全工作流/引擎和文档检查仍留本轮收尾。
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
'hub/records/**/*.test.ts' hub/knowledge/personal-import-scope.test.ts hub/knowledge/personal-mount-recovery.test.ts \
hub/knowledge/personal-purge.test.ts hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts \
packs/workflow/case-review/packs.test.ts tests/acceptance/46-knowledge-spaces.test.ts \
tests/acceptance/47-knowledge-sharing.test.ts tests/acceptance/48-knowledge-console-memory.test.ts tests/acceptance/49-personal-sync.test.ts
第 2 步安全审查修正 5:清除后的个人路径显示
- 采用审查允许的显示遮盖,不重写历史回执。共享回执、团队条目来源及来源链接在个人来源代次已清除时显示“已清除的个人文件夹”,不再输出原路径、带原路径的引用或不安全的旧自由说明;团队实际目标目录、团队正文及安全回执事实保留。原始回执仍受既有 rec: 拒读保护。
- 文案直接取自 Claude 审查第 5 项,加入既有 sync_field 显示信息,模型按规则升为 26 并生成;同步方式代码、记录和权限规则不变,固定模型版本测试同步。生成器仅改 vocabulary.ts/protocol.ts,四技能协议镜像未变化,因此不改包版本或业务代码。
- 新增元数据清除标记端口,界面只查摘要绑定和清除日志,不加载私人正文;PageContext 生命周期内合并同一来源标记读取。独立 PG 回归证明管理员和本人都不再看到原路径,回执原始字节未变;真实 Hub 的用户名/口令控制台回归同时检查团队目录与正文保留、原始回执和旧个人引用仍被 MCP 拒读。
- 首轮界面组 4 项中 3 通过,拒收夹具误用了未登记的代码而被 Schema 排除;改用合约已有 SHARE_UNVERIFIED,保留原遮盖和回执断言。
验证:类型检查、生成/合约核对(175 文件)、差异检查通过。下列定向组 456 项通过、0 失败、0 跳过;新增词表断言复查 9 项通过,最后含真实控制台回归的组 10 项通过。真实控制台测试的 URLSearchParams 夹具初次类型检查不接受可选 undefined,改为明确的字符串键值表后通过,运行行为不改。PG 18 ownership 与 macOS 沙箱项实际运行。没有改变迁移、运行或依据判定;未跑全量回归/Linux CI/来源检查,文档检查留本轮收尾。
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" node --test --test-concurrency=1 --test-reporter=tap \
'contracts/src/*.test.ts' hub/server/console/space-records.test.ts hub/server/console-sync-privacy.test.ts \
hub/server/console-knowledge.test.ts hub/server/console-sharing.test.ts \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts hub/records/lock-namespaces.test.ts \
packs/workflow/case-review/packs.test.ts tests/acceptance/46-knowledge-spaces.test.ts \
tests/acceptance/47-knowledge-sharing.test.ts tests/acceptance/48-knowledge-console-memory.test.ts tests/acceptance/49-personal-sync.test.ts
最终复查:node –test –test-concurrency=1 –test-reporter=tap contracts/src/vocab-axes.test.ts hub/server/console-sync-privacy.test.ts。
第 2 步安全审查修正 6:启动遍历与索引边界
做了的:按账户、代次和对象摘要记住已复制/遍历节点,两组集合各限 50,000 项且不缓存正文;跨账户仍各自复制。已转换的私人原件/目录不进入独立持有者索引。所有者完成函数限制目录核对为 10,000 个节点、64 层,超过即拒绝完成,不截断成功。迁移 14 未部署,复核后同步迁移源快照,1~13 与 15 的 SQL 不变。
验证:新增 1,200 个历史版本、两个账户、四个共享 DAG 节点和 8,000 个无关持有者的升级测试,验证精确查询使用 object_holds_target。修复前读取 6,005 次、转移约 3.98 秒;修复后读取 8 次、转移约 0.44 秒(本机合成测量,不是生产时间保证)。类型检查与差异检查通过;personal-retirement-bounds、personal-retirement-holds、personal-mention-retirement、personal-upgrade、personal-runtime-guards、migrations.snapshot、environment、diagnostics、PG 18 ownership、lock-namespaces 定向组共 38 项通过、0 失败、0 跳过。未跑全量回归、Linux CI、来源检查;文档检查留本轮收尾。
第 2 步安全审查修正 7:按来源摘要核对清除
做了的:团队 src 依据改用既有 knowledge.space.source_hash 精确索引,只加载匹配来源并核对其是否属于该版本;不再逐条读取全部团队来源。清除判定只读绑定/清除元数据,树依据也不加载私人正文。不设跨请求的否定缓存,清除立即生效。未改迁移或历史内容。
验证:400 个真实来源的回归核对匹配加载数、零逐来源 get、后续清除立即变为待验证且团队正文不变。首轮夹具误给条目加 path 字段,修正后又发现把 put 的 created 返回标记当成存储内容比较;改为比较实际正文,两项夹具修正均未放宽产品断言。类型检查通过(泛型计数包装同步保留原端口类型)。personal-ground-index、personal-purge、environment、diagnostics、PG 18 ownership、lock-namespaces、验收 47/49 共 34 项通过、0 失败、0 跳过。全部工作流/引擎在本轮依据相关修复完成后统一运行;全量回归、Linux CI、来源检查未运行,文档检查留收尾。
第 2 步安全审查修正 8:退役目录先同步落盘
做了的:无独立持有者的旧文件删除后,严格打开并 fsync 所在目录,成功后才调用所有者退役完成函数。同步或关闭失败直接中止启动,保留待处理日志;重启遇到已删除文件时仍重新同步目录后再完成。不改普通对象写入的既有行为。
验证:故障注入在 unlink 之后使目录同步失败,断言文件已不在、完成日志未写、清除被阻止;新实例恢复时执行真实目录 fsync,完成前再次核对日志不存在,随后清除成功。类型与差异检查通过;personal-retirement-durability、personal-upgrade、personal-mount-upgrade、personal-retirement-holds、environment、diagnostics、PG 18 ownership、lock-namespaces 共 31 项通过、0 失败、0 跳过。未跑全量回归、Linux CI、来源检查,文档检查留收尾。
第 2 步安全审查修正 9:本机方式的空视图与返回入口
做了的:
- 个人方式只读查询不创建正文。清除后的个人知识/记忆列表为空;控制台在本人“个人/全部”和个人目录入口显示生成词表中的“同步方式/仅保存在本机”,提供“同步到 Hub”入口,隐藏不能使用的个人写入/授权控件。旧表单写入给出既有方式错误,不变成内部错误;团队内容仍可读。
- MCP 的默认本人空间、显式本人空间及本机方式下启动的运行本人空间返回空节点;个人搜索和记忆页为空。显式旧私人引用保持不存在/无权同形;不恢复已清除内容或旧授权。
- 为使这些用户仍可启动团队工作流,运行的 SpaceLocks.personal 允许 null,明确没有个人根;团队根照常锁定。重新生成三份受影响 Schema,六工具数量与权限不变,没有新增显示轴;四技能镜像未变化,不升级技能包。依据读取对空根明确处理,保留可选知识端口兼容。
- 本人密码会话的返回入口只接收该主体的期望代次与状态,不接受目标账户。运行角色仍无 personal_spaces UPDATE,通过固定搜索路径、撤销 PUBLIC 执行权的所有者函数检查已完成的清除和新 UUID 后切换;旧代次不能复用。方式、空头与仅含元数据的审计同事务,失败全部回滚;停用账户、旧页面及竞争请求拒绝。此处只做审查要求的 local → hub 返回入口;完整双向方式预检、清除 HTTP、原生同步 CAS 仍属于后续步骤,不提前开放。迁移 14 尚未部署,按授权编辑并复核源快照;1~13、15 SQL 与第 3 步完全一致。
- 新增运行角色与真实控制台/MCP 回归,核对故障回滚、并发只成功一次、无 UPDATE 权限、旧客户端拒绝、CSRF、管理员不能替他人切换、旧授权不复活、团队记忆与工作流保留。扩展工作流夹具时先后修正了 assign 的字符串参数次序及遗漏的 knowledge.json 目录要求;均保留原产品断言。
最终验证:类型检查、生成及 175 文件合约核对、差异检查通过。全部工作流/引擎 2,765 项通过、0 失败、0 跳过;macOS 沙箱可用且用例实际执行。下列扩大定向组首跑 726 项中 725 通过、1 失败、0 跳过:验收 46 仍要求提示包含“服务器备份”,调整说明时误删该措辞;恢复为“同步到 Hub 的个人内容保存在 Hub 数据库和服务器备份中”,原断言不改。受影响的控制台/验收 46 复查 31 项全部通过、0 跳过;没有把首跑整组写成全绿。其余用例的通过结果保留,不重复累加计数。
扩大组包含全部 records(含 password、锁命名及迁移快照)、PG 18 ownership、environment、diagnostics、新运行角色/真实控制台回归、合约与受影响知识/记忆/控制台及验收 46~49。49,000 条目知识视图测试通过,未修改其固定预期;大库退役复查仍为 8 次节点读取,本次扩大组测得约 1.90 秒,说明耗时随本机负载变化,前述 0.44 秒不是普遍保证。源比较确认迁移 1~13 和 15 与第 3 步完全相同;没有修改其他已应用 SQL。
文档检查在暂存新增文件后通过 291 项、0 跳过;填入实际结果后的收尾复查同样为 291 项通过、0 跳过。已运行范围无遗留跳过。未运行全量 unit/acceptance、Linux CI、来源归档检查(来源工具/批准导入未改);不把它们算通过。不推送、不建 PR、不部署。第 4 步尚未开始。
npm run typecheck
npm run gen:contracts
npm run verify:contracts
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
'packs/workflow/**/*.test.ts' 'hub/runs/engine/**/*.test.ts'
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
'contracts/src/*.test.ts' 'hub/records/**/*.test.ts' \
hub/knowledge/spaces.test.ts hub/knowledge/spaces-review.test.ts hub/knowledge/memory.test.ts \
'hub/knowledge/personal-*.test.ts' hub/knowledge/view-perf.test.ts \
hub/server/console.test.ts hub/server/console-knowledge.test.ts hub/server/console-memory.test.ts \
hub/server/console-sharing.test.ts hub/server/console-search.test.ts hub/server/console-sync-local.test.ts \
hub/server/console-sync-privacy.test.ts hub/platform/environment.test.ts hub/refs/diagnostics.test.ts \
hub/platform/ownership.test.ts tests/acceptance/46-knowledge-spaces.test.ts \
tests/acceptance/47-knowledge-sharing.test.ts tests/acceptance/48-knowledge-console-memory.test.ts tests/acceptance/49-personal-sync.test.ts
# After restoring the existing backup wording; original assertions unchanged.
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" node --test --test-concurrency=1 --test-reporter=tap \
hub/server/console.test.ts hub/server/console-knowledge.test.ts hub/server/console-sync-local.test.ts tests/acceptance/46-knowledge-spaces.test.ts
npm run test:docs
第 4 步:原子提交、确定性冲突与重新核验
来源:Claude 收到 958ead07 与八项审查修正后要求继续第 4 步;复核新发现按随后指令在本步提交之后、第 5 步之前处理。
做了的:
- 追加迁移 16,保存私有提交/核验观察、原生映射、容量抵扣与记忆冲突预留;原生知识、记忆、同步头、回执、审计在同一本人锁及事务中推进。运行角色不获 personal_spaces UPDATE,使用固定搜索路径的所有者函数取得范围锁并替换本人记忆投影。
- 状态包含知识、记忆和个人包清单变化。控制台与 MCP 写入后旧状态不能提交;两个真实同步会话竞争只有一次成功。失配保留暂存,返回本人新状态,不自动覆盖;重复操作返回冻结回执,不重复计费或核验。
- 从既有内容导出稳定 ID、原生映射和只读外部依据;共享向量补充空原文、未知文件名和同批前件。便携祖先合并不使用时钟/到达顺序;同项分叉、删除对修改、同名目录、目录删除后新增子项均保留冲突。解决必须接续全部竞争头;不从缺项猜删除。
- 本人可显式读取历史;未解决候选不进入默认知识投影或记忆加载,答复列出冲突。记忆以最大候选预留标题+路径+正文及一篇额度,所有候选计历史容量。投影锚点和后续原生记录支持重建,删除/冲突不会复活。
- 重新检查目录、当前读权、解析器和逐字引文,核验观察与原对象分开,带规则、扫描器、解析器锁及有界位置。不可读与不存在同形;同内容重交可产生新观察。未核验/受限内容仅本人可读,授权目录也不能扩大资格,搜索在分页前排除。
- 同批知识前件生成真实历史原生根;原文和引文字节保真,保留采集作者、读取级别、媒体类型和未知文件名,不把个人凭据来源变成团队资料。可编辑文字不额外裁剪尾随空白。相同原生路径下不同稳定身份的同内容条目无法用既有地址区分,保留为结构冲突而非静默合并。
- 原生历史、暂存对象、私有回执与客户端元数据共同计量;导出/投影抵扣避免重复收费。个人包发布也取得本人空间锁;清单同步只报告已有安装,不安装、不发布、不执行。异步原生写入重查代次;新控制台表单带隐藏代次,清除并返回 Hub 后旧表单拒收。
- 生成 Schema;无新增词表、配置、协议镜像或技能补丁版本,模型保持 26,MCP 仍六工具。HTTP 和完整方式切换仍属第 5 步,未提前开放。
核验记录:先前核心组 17 项、扩大定向组 683 项通过,均无跳过;随后补充旧表单回归,并改为逐份加载原件、立即暂存导出字节块,避免保留整空间原文副本。最终组另行记录,不重复累加中间计数。首次夹具错误包括 search 回执字段、memory_write 回执字段、不完整包文件清单、原地排序改动目录顺序和类型别名误用;均修正夹具而非放宽断言。运行角色恢复曾因直接 FOR SHARE 需要 UPDATE 权限失败,改为所有者范围锁,不授予 UPDATE。最终迁移源对比先用正则遗漏常量初始化,改用 TypeScript AST 后确认 1~15 原有 SQL 源均相同。
工作流/引擎扩大组在尚未结束时主动中断:因为紧随本步的复核修正还要改变运行原件读取,完整组留到修正后统一执行,避免重复运行;中断组不计通过。未改现有知识视图固定预期。来源工具/批准导入未改,不运行来源检查;全量 unit/acceptance 与 Linux CI 未运行。没有推送、PR 或部署。
第 4 步最终验证:扩大定向组 745 项通过、0 失败、0 跳过,含所有 records(迁移源快照、密码、锁命名、运行角色、升级与备份)、合约、知识/记忆、控制台、环境变量与缺省知识端口、PG 18 ownership、验收 46~49。知识视图 49,000 条目用例通过且未更改固定输出预期。收尾补充竞争失败快照可读性、结构冲突随原生更新保留,以及逐份加载/暂存后,核心与真实 Hub 验收 49 复查 18 项通过、0 跳过。类型检查、175 文件合约核对、差异检查通过;共享编码/合并组 8 项通过。文档检查首轮及填入结果后的收尾复查均为 291 项通过、0 跳过。完整工作流/引擎组保留到紧随本步的安全修正后执行;当前中断组没有按通过报告。
npm run typecheck
npm run gen:contracts
npm run verify:contracts
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
'contracts/src/*.test.ts' 'hub/records/**/*.test.ts' \
hub/knowledge/spaces.test.ts hub/knowledge/spaces-review.test.ts hub/knowledge/memory.test.ts \
'hub/knowledge/personal-sync/*.test.ts' 'hub/knowledge/personal-*.test.ts' hub/knowledge/view-perf.test.ts \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts \
hub/server/console.test.ts hub/server/console-knowledge.test.ts hub/server/console-memory.test.ts \
hub/server/console-sharing.test.ts hub/server/console-search.test.ts hub/server/console-sync-local.test.ts \
hub/server/console-sync-privacy.test.ts tests/acceptance/46-knowledge-spaces.test.ts \
tests/acceptance/47-knowledge-sharing.test.ts tests/acceptance/48-knowledge-console-memory.test.ts tests/acceptance/49-personal-sync.test.ts
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" node --test --test-concurrency=1 --test-reporter=tap \
hub/knowledge/personal-sync/commit.test.ts tests/acceptance/49-personal-sync.test.ts
npm run test:docs
复核修正 1:所有者函数的临时类型隔离
做了的:按 Claude 对 b3b97cce 的复核要求,把所有 SECURITY DEFINER 的 search_path 固定为 pg_catalog,public,pg_temp;参数、返回与局部类型限定到 pg_catalog,表行类型限定到 public。迁移 14~16 均未部署,按本次修正指令复核修改与迁移源固定值;迁移 1~13 不动,转换豁免与运行权限不扩大。审查页未提交的“复核”附录原样加入本次提交。
验证:把审查探针改写为仓库中的 personal-definer-security.test.ts,不从临时目录导入。修正前真实运行角色通过临时 text 域把合成只追加记录改成 rewritten,回归按预期失败(1 失败,不算通过);修正后要求正文与逻辑摘要保持原值,并从 pg_proc 核对每个所有者函数的搜索路径。修正后 personal-definer-security、personal-runtime-guards、personal-resume、migrations.snapshot、lock-namespaces、environment、diagnostics 与 PG 18 ownership 定向组 25 项通过、0 失败、0 跳过。首次类型检查有两处探针查询返回值未声明类型;补齐后 npm run typecheck 通过,差异检查通过。文档检查留本轮收尾;工作流/引擎完整组留三项修正之后。
复核修正 2:拒绝运行角色的临时对象权限
做了的:grantRuntime 在授权任何表/函数前检查数据库 TEMPORARY 权限,verifyMigrations 在运行连接启动校验时再次检查;PUBLIC、直接授权与角色继承均按实际权限拒绝。不在授权函数里悄悄收回数据库权限。拥有表的迁移连接仍可离线校验;grantRuntime 原有的拒绝表所有者规则保留。五处真实运行角色夹具先显式收回合成数据库的 PUBLIC TEMPORARY,原产品断言不变。没有改配置或迁移 SQL,本项迁移文件摘要仅因权限自检代码改变而同步。
验证:personal-runtime-temp 在修正前因缺少拒绝而失败(1 失败,不算通过),修正后覆盖 PUBLIC/直接/继承权限、无部分授权、运行角色不能建临时表、权限恢复后可校验。包含 runtime-temp、definer-security、runtime-guards、resume、sync、upgrade、迁移快照、password、lock-namespaces、environment、diagnostics 与 PG 18 ownership 的定向组 42 项通过、0 失败、0 跳过;最终 npm run typecheck 和差异检查通过。完整工作流/引擎与文档检查留下一项后统一运行。
复核修正 3:运行独立持有的个人上传
做了的:
- 追加迁移 17 的 run_originals,保存运行已经绑定的个人上传之原采集正文和原始字节,保留原逻辑摘要、采集作者、读取级别与媒体类型,不改原 records 行。独立副本属于运行持有,不随个人代次清除;个人空间的来源链接、目录授权与其他未被运行持有的内容照常清除。
- 由固定搜索路径、限定类型、撤销 PUBLIC 执行权的所有者函数,在实际 run 追加事务中按精确 artifact 绑定保留;运行角色不能直接写表或调用保留函数。失败回滚不留下独立持有者。迁移按同一函数补齐已有运行;已在更早清除中消失的字节不能凭摘要恢复。
- 读取仍经原有采集与运行权限/凭据档位检查。保留只作用于存储,不产生新的上传署名、不扩大原件 ACL、不把个人原件变成普通团队导入声明;采集记录的 personal_scope 持续阻止清除后被误认为普通上传。其他成员的私人引用、知识条目、个人记忆及非上传来源没有获得额外权限。既有 legacy 运行原件保留路径不改。
- 新表只授运行角色 SELECT,并登记 ownership 列;迁移 1~16 SQL 不变,复核新增 SQL 后同步源文件固定值。同步审计不存原件正文,六工具与同步对象合约不变。
验证记录:首次新夹具把个人采集与来源链接分两次提交,被已有“必须有本人来源链接”的约束拒绝;改为同一事务后,修正前确实复现运行采集随清除消失(回归 1 失败,不算通过),没有放宽约束。
第一轮核心 18 项通过仍不足以证明读权边界,随后扩大组发现下述权限误差。收窄为只保留存储后,artifacts、roles、personal-run-originals、production-guard 与验收 46/48/49 专项复查 41 项通过、0 失败、0 跳过;原读权断言全部保留。
四项新存储回归覆盖失败回滚、已有运行补齐、运行角色间接保留/直接调用拒绝与无持有者清除。真实 Hub 个人工作流回归证明本人读取不丢失、管理员及无关成员仍拒读、不生成普通上传署名。PG 18 ownership 的备份/销毁/恢复新增原件断言通过:恢复旧备份可恢复个人链接,随后清除链接而运行原件精确字节仍可读。类型检查通过;最终扩大组与完整工作流/引擎按收窄后的代码再跑,结果另记。
首轮完整工作流/引擎组为 2,764 通过、1 失败、0 跳过;首轮扩大组为 750 通过、4 失败、0 跳过。读权失败揭示实现曾把“独立持有”扩大成自动向运行读者新增个人上传署名,这超出了保留原件的范围。已撤回这一权限变化,恢复 production-guard、artifacts 和验收 46/48 的原始断言,输入绑定代码保持原样;新验收改用个人工作流,证明本人仍能在原运行读取、管理员与无关成员无权读取,且不会生成普通上传署名。不把失败归为应当放宽的旧期待。
扩大组还发现 roles.test.ts 经 migrateRecords 间接授权的夹具保留了 PostgreSQL 默认 PUBLIC TEMPORARY,按新要求应拒绝。已同其他四处角色夹具一样先收回这一默认权限;原运行角色读写、只追加保护、所有者拒绝与恢复断言均不改。该环境修正并入复核修正 2 的本机提交,首跑失败与定向复查分开记录。后续结果另记。
本轮最终验证与停止点
- 收窄后的最终代码:全部工作流/运行引擎 2,765 项通过、0 失败、0 跳过,macOS 沙箱用例实际执行。
- 最终扩大定向组 754 项通过、0 失败、0 跳过。包括所有合约与 records、三项复核回归、原生产与读权拒收断言、知识/记忆/控制台、环境变量、缺省知识端口、锁命名与密码迁移,以及验收 46~49。
- PG 18 ownership 的 3 项实际执行,包含备份/销毁/恢复后私人链接可再次清除、独立运行原件精确字节仍可读及运行角色不能篡改的断言。
- 知识视图 49,000 条目用例通过,固定输出预期没有修改。迁移 17 仅追加;与复核修正 2 比较,迁移 1~16 SQL 源保持原样。没有新增全库 SHA 扫描。
- 最终 npm run typecheck、175 文件合约核对与差异检查通过;填入结果后的文档复查 291 项通过、0 跳过。
- 此前误扩大署名范围、遗漏的角色夹具及探针类型错误均已如上记录;失败与主动中断的运行没有算作通过。最终保留原署名、读权及既有测试断言,未增加管理员隐含读取权。
- 已执行用例没有遗留跳过。未运行全量 unit/acceptance、Linux CI、来源归档检查;来源工具/批准导入未改。未推送、创建 PR 或部署。第 5 步尚未开始。
npm run typecheck
npm run verify:contracts
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
'packs/workflow/**/*.test.ts' 'hub/runs/engine/**/*.test.ts'
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
'contracts/src/*.test.ts' 'hub/records/**/*.test.ts' \
hub/knowledge/spaces.test.ts hub/knowledge/spaces-review.test.ts hub/knowledge/memory.test.ts \
'hub/knowledge/personal-sync/*.test.ts' 'hub/knowledge/personal-*.test.ts' hub/knowledge/view-perf.test.ts \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts \
hub/runs/engine/production-guard.test.ts hub/server/console.test.ts hub/server/console-knowledge.test.ts \
hub/server/console-memory.test.ts hub/server/console-sharing.test.ts hub/server/console-search.test.ts \
hub/server/console-sync-local.test.ts hub/server/console-sync-privacy.test.ts \
tests/acceptance/46-knowledge-spaces.test.ts tests/acceptance/47-knowledge-sharing.test.ts \
tests/acceptance/48-knowledge-console-memory.test.ts tests/acceptance/49-personal-sync.test.ts
npm run test:docs
git diff --check
第三轮复核修正:记录摘要读取保持索引执行计划
来源:Claude 对 0ab652a8 的第三轮独立复核,以及要求先修复可用性问题、再继续第 5 步的指令;审查原文随本次提交收入 kw-sync-review.md。
做了的:
- 暂存尚未完成的第 5 步,不把 HTTP/方式切换的修改混入本项。只移除迁移 17 中 hub_read_records() 的 SET search_path 子句;该函数按调用者权限执行,返回类型、表和函数调用已限定模式名。保留所有 SECURITY DEFINER 的固定搜索路径与权限限制。
- 将审查探针改写为仓库内的 read-plan.test.ts,不从审查临时目录导入。写入 50,000 条合成记录,以实际 EXPLAIN ANALYZE 的 JSON 执行计划检查:已存在和不存在的摘要,在迁移所有者与运行角色下都走 records_digest_key 的 Index Scan;拒绝 Function Scan/records 全表扫描,底层记录读取至多一行。保留真实 store.get 的返回值断言,不使用强制规划设置或易受机器负载影响的耗时门槛。
- 迁移 14、15 从本项起冻结,将当前 SQL 身份加入既有固定快照;本项没有修改这两个迁移,也没有新增数据 SHA 扫描。复核迁移 17 的单处更正后同步迁移源文件的固定摘要;迁移 1~16 SQL 保持原样。
- 更新 records README 与 CHANGELOG;审查附录保留原文。
验证:修正前新回归真实失败 1 项(Function Scan),修正后独立运行 1 项通过、0 失败、0 跳过;初跑失败不算通过。最终定向组 287 项通过、0 失败、0 跳过,含全部 records(新执行计划回归、SECURITY DEFINER、运行角色、原件保留、迁移固定快照、密码与锁命名)、environment、diagnostics、PG 18 ownership 与验收 46~49;PG 18 备份/恢复与 macOS 沙箱项实际执行。npm run typecheck、git diff –check 通过,文档检查 291 项通过、0 跳过。未运行全量 unit/acceptance、全部工作流/引擎、Linux CI、来源归档检查;本项没有改运行/依据规则或来源工具。未推送、建 PR、部署。第 5 步的工作仍单独保留,不算本项完成。
npm run typecheck
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
'hub/records/**/*.test.ts' hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts \
'tests/acceptance/46-*.test.ts' 'tests/acceptance/47-*.test.ts' 'tests/acceptance/48-*.test.ts' 'tests/acceptance/49-*.test.ts'
npm run test:docs
第 5 步:HTTP 同步、同步方式与安全审计
来源:Claude 收到 a266c5e9 与三项复核修正后要求按第 13 节继续;本步进行中收到第三轮可用性复核,先暂停并单独提交 10f92a91 的索引修复,再恢复本步。未修改已冻结的迁移 14、15。
做了的:
- 在原 HTTP/HTTPS 监听上接入独立的 /api/sync/v1 合约路由:状态、客户端、会话、冻结清单、缺件查询、上传/下载、原子提交、操作回执、终止,以及同步方式检查/切换。不增加 MCP 工具或模型可调用的代理。沿用 Host/HTTPS/代理权限,Origin 限本服务来源,不开放 CORS,答复 no-store。
- records 认证端口区分访问 Bearer 和控制台会话令牌;访问令牌及 OAuth 令牌沿用当前撤销、期限与停用检查。同步 HTTP 不接受 Cookie、控制台会话 Bearer 或设置码。审计中的 OAuth 客户端绑定从服务器令牌记录取得,设备 UUID 单独记为客户端声明;不记录令牌原文或设备名称。处理前、读完正文及发送答复前再次检查认证;会话所属检查在大正文解析前进行,私有答复发送前再检查代次/方式。
- 按账户限流,同一账户换令牌或设备不能绕过,HTTP/HTTPS 共用。只增加已批准的 CIRCLE_HUB_SYNC_REQUESTS_PER_MINUTE,默认 120、范围 1~10,000,同时登记 config 与平台清单;同时最多两次写请求。请求与成功答复共用既有全局字节预算,保持批量/对象及线路大小上限;重复 JSON 键、无效 UTF-8、压缩正文、未知字段/查询参数均拒收。429 返回既有 SYNC_RATE_LIMITED 与 Retry-After。
- 本人切换方式由控制台自己的口令会话、同源与 CSRF 路由调用同一服务;同步 HTTP 方式路由仅用 Bearer。仅保存在本机前必须重新检查影响并明确确认。检查证明绑定当前状态、暂存批次/会话/操作及目录授权/共享观察,未改变内容头的暂存和授权也会使旧检查失效。清除、方式、回执和审计在同一事务,失败整体回滚;团队副本保留、本机副本不受影响,页面说明旧备份恢复边界。
- 恢复同步调用既有所有者函数 hub_resume_personal 创建新代次,不给运行角色 personal_spaces UPDATE。新增迁移 18 只存按本人/代次/操作标识绑定的无正文方式回执;旧清除请求可重复取原回执,不会删除新代次内容。新表运行权限只有 SELECT/INSERT,登记 ownership 列;迁移 1~17 SQL 保持索引修复后的原样,复核追加 SQL 后同步源固定值。
- 个人知识/记忆视图显示生成的同步方式/状态、冲突数量与上次同步;成员详情给管理员显示同步方式与上次同步,不显示设备或私人标题。没有布局打磨或 Circle 端实现。现有只读共享检查没有持久化计数,因此合约的 pending_shares 允许 null,页面据实说明确认时重新核验,不把未知写成 0。没有新增显示轴,模型仍为 26;生成新增方式结果 Schema,176 个生成文件,四技能镜像/版本不变。
- 每个已认证请求追加开始/结束安全元数据:操作人、服务器令牌标识与 OAuth 客户端、声明设备 UUID、路由模板、时间、对象计数、状态身份、实际 HTTP 状态/拒收码。标题、目录、原文、引文、设备名称与异常原文不入审计/日志。开始审计失败则不做操作,结束审计失败返回可重试错误,已提交的操作经幂等回执恢复;预算拒收记实际的 503/413,不能记成成功。
- 更新 config、records、knowledge、server README 和 CHANGELOG。第 6 步、决定 138 的完成记录、Circle 仓库、推送、PR 与部署均未开始。
验证记录:HTTP/配置首组 24 项通过;新增方式/HTTP 组首轮 9 项中 8 通过、1 失败,原因是夹具把同步抛出的结构错误直接传给 assert.rejects,改为异步调用包装,保留原拒收断言;扩大新回归 11 项随后通过。最初配置类型检查遗漏 names.ts 的变量登记,补齐 config 与平台登记及配置回归后通过。暂存恢复时迁移源固定值有冲突,保留第三轮固定的 14/15 快照并按追加 18 的实际源重新复核。上述中间失败不算通过,最终结果另记。
补充验证记录:最初 HTTP 接线的三个新回归曾因读取了错误的请求频率配置路径而返回 503,补齐 limits 正式字段、config 名称与平台登记后,原 4 项组复查通过,再执行上述 24 项组。没有把早期失败记作通过。HTTP 审计使用单独的 personal-sync-http-audit@1,避免与已有 HTTP 路由清单的 personal-sync-http@1 同名。收尾检查发现普通提交回执也可能含私人路径,已将清除后的跨代次例外严格限于无正文的方式回执,并补真实清除竞争回归;下载中撤销令牌同样拒绝交付。
第 5 步最终验证与停止点
- 扩大定向组 810 项通过、0 失败、0 跳过:合约、全部 records(包含 14/15 固定 SQL、源快照、索引计划、密码、运行角色、升级、锁命名)、配置、个人同步服务、知识/记忆、environment、diagnostics、PG 18 ownership、控制台与 HTTP、工作流包静态约束、验收 46~49。PG 18 备份/恢复与 macOS 沙箱项实际执行。该命令还误带了不存在的 hub/server/shutdown.test.ts,Node 没有执行这个文件,不计为通过;关闭边界实际在已执行的 hub/server/http.test.ts 中,收尾命令仅列现有文件。
- 最终适配器与清除/撤销竞争回归组 107 项通过、0 失败、0 跳过,含所有新增 HTTP/方式回归、MCP、标准授权、HTTP 关闭边界、真实 Hub 验收 49;与扩大组有交集,不把两组相加当作独立用例数。
- 最终 npm run typecheck 通过;npm run gen:contracts 与 npm run verify:contracts 通过,176 个生成文件一致。没有模型或四技能业务代码/镜像变化。
- 文档检查及填入结果后的收尾复查均为 291 项通过、0 失败、0 跳过;差异检查通过。
- 已执行用例无遗留跳过。未运行全量 unit/acceptance、完整工作流/引擎、Linux CI、来源归档检查;本步未改运行/依据规则或来源工具,没有增加全库 SHA 校验。Circle 端未实现,不把 Hub 本机合成验收当作客户端已接通。
- 只作本机签署提交,不推送、建 PR 或部署;第 6 步和决定 138 的完成记录均留待下一步。
npm run typecheck
npm run gen:contracts
npm run verify:contracts
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
'contracts/src/*.test.ts' 'hub/records/**/*.test.ts' 'hub/config/*.test.ts' \
'hub/knowledge/personal-sync/*.test.ts' hub/knowledge/personal-purge.test.ts hub/knowledge/memory.test.ts hub/knowledge/spaces.test.ts \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts \
hub/server/personal-sync.test.ts hub/server/console-sync-local.test.ts hub/server/console.test.ts \
hub/server/console-knowledge.test.ts hub/server/console-memory.test.ts hub/server/console-password.test.ts \
hub/server/console/member-space-metadata.test.ts hub/server/http.test.ts hub/server/shutdown.test.ts \
packs/workflow/case-review/packs.test.ts \
'tests/acceptance/46-*.test.ts' 'tests/acceptance/47-*.test.ts' 'tests/acceptance/48-*.test.ts' 'tests/acceptance/49-*.test.ts'
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
hub/server/personal-sync.test.ts hub/server/mcp.test.ts hub/server/standard-authorization.test.ts hub/server/http.test.ts \
hub/records/personal-sync-mode.test.ts tests/acceptance/49-personal-sync.test.ts
npm run test:docs
git diff --check
第 6 步:共享样例冻结、定向验收与决定 138
来源:Claude 收到 10f92a91 与 5509e847 后要求继续第 6 步;随后明确要求完整 unit 与 acceptance 各跑一次并顺序进行、报告每个失败和跳过。这一要求覆盖本轮原有的“仅定向检查”限制,不回写先前未运行项为通过。
做了的:
- v1 的 encoding、scenarios、native-links 和 HTTP 路由样例保持原字节;新增独立编写的 exchange.json,围绕既有五个记忆修订补齐固定目录、清单与快照祖先,以及保真原文、引文和知识核验情景。README 标记版本冻结、跨端消费方式与兼容性变更边界;不新增全库 SHA 校验或测试时自动重生成期待值。
- 合约测试检查规范字节与对象图闭包;场景 49 直接上传冻结字节,经真实 HTTP 运行两客户端离线冲突/CAS 拒收/明确解决,并检查 MCP 会话不载入冲突候选。另一场景保留 BOM/CRLF 原文及编辑文本尾随空白,服务端重新核对正确/伪造引文,分别得到已验证/待验证事实,不采信客户端。
- 保留原有场景 49 的两端传输/续传、跨账户拒读、只同步清单、控制台过期表单、清除/新代次,以及运行独立原件读权断言。阶段 4 的容量、凭据、只追加、审计与模式切换故障注入仍由现有 records/knowledge/server 回归覆盖。
- 更新 contracts、server、records README、CHANGELOG 和进度页决定 138,明确来源、协调答复、Hub/Circle 分界及未部署事实。没有新模型、迁移、运行代码或 Circle 改动。独立审查仍在进行,收到后续发现另行处理。
验证记录将在实际运行结束后填写;完整 unit 与 acceptance 不并行、不提前将尚未运行的检查计作通过。
完整 unit 首跑:全部失败和跳过
按本轮明确要求仅运行一次完整 unit,三个内部分组全部结束,约 1,360 秒,退出 1;合计 8,783 项:8,682 通过、92 失败、9 跳过、0 取消。三个分组分别为 shared 8,378(8,359/12/7)、isolated 215(133/80/2)、sentinel 190(190/0/0);括号内依次为通过/失败/跳过。没有把两个成功分组或原定向结果当作整个 unit 通过。
92 项均在 CLI 测试部署的迁移/启动阶段失败;明确报错为运行角色仍有创建临时对象的权限,run-list 的四个用例由同一 before 钩子的迁移失败连带触发。夹具使用默认 PUBLIC TEMPORARY,而复核已要求 grant/verify 阶段拒绝此权限。当前不改生产拒绝、不改迁移 14/15、不改业务断言;完整 acceptance 已在 unit 结束后单独启动。修正夹具及定向复查结果另记。
| 文件 | 失败数 |
|---|---|
| hub/server/assemble/cli-admin.test.ts | 59 |
| hub/server/cli.test.ts | 13 |
| hub/server/command-bridge.test.ts | 4 |
| hub/server/run-list.test.ts | 4 |
| hub/server/assemble/assemble.test.ts | 6 |
| hub/server/assemble/cli-run-abandon.test.ts | 1 |
| hub/server/assemble/cli-skill-migration.test.ts | 4 |
| hub/server/key-transfer.test.ts | 1 |
以下逐项保留首跑失败,不合并成一次失败;名称与首跑位置供定位,不保留原始日志/堆栈或本机绝对路径。
hub/server/assemble/cli-admin.test.ts
- 204:user list JSON distinguishes an empty database and existing administrators without exposing password material
- 218:password-reset recovers the sole administrator before the first change and revokes console sessions only
- 245:password-reset refuses missing, invalid and member operator tokens and a forged –as without changing history
- 259:initial passwords use stdin when supplied and otherwise appear once with a forced first change
- 278:an existing administrator can reissue into the Linux file store without displaying the token
- 299:init-admin creates the first admin and stores its token without printing it; it is refused once an admin exists
- 318:init-admin refuses when the store already holds a token, and creates nobody
- 326:when the token cannot be stored, the admin exists, its token is revoked and the fix is operator-token reissue, which needs no token
- 347:when the token cannot be stored and its revocation is not confirmed, init-admin says the token may be live and names token revoke
- 419:a lost keychain item is recovered without any token: operator-token reissue stores a new admin token, never shown, and every refusal on the way names it
- 468:revoking the operator token itself is done and said, with the way back; a dead item is replaced by a reissue once it is removed
- 509:disabling the operator token’s own account is said, with the way back through another admin
- 523:operator-token reissue names its admin among several, refuses a user and revokes a token it cannot store
- 561:operator-token set stores an enabled admin’s token read from stdin; it refuses an existing item, a user’s token and anything invalid
- 598:every state-changing command is refused without a token, with a non-admin token and with an invalid one
- 645:CIRCLE_HUB_TOKEN acts for the command and is taken out of the environment before the configuration is read
- 663:read-only commands act as the token’s account: –as must name that account; without a token –as or the only admin
- 693:a keychain item that cannot be read costs a read-only command a notice, not the listing; a changing command is refused naming the operator token
- 734:a held-back source is listed, its findings shown as positions and shapes, accepted with a note and withdrawn again; no output carries the value
- 757:source commands distinguish current direct, derived and absent acceptance without disclosing matches or fingerprints
- 785:a user who is not an administrator, a bad digest and a note that copies the value are refused
- 814:list shows the unexpired rows of a prefix with their holders and ends, and nothing without one
- 827:release frees the key with a reason; the reason lands in the action record; –reason is required
- 846:a user who is not an administrator gets the same answer as for a row that does not exist
- 879:lists read originals with external id, digest, size and read time, filtered by prefix, without content
- 899:an unmatched suffix names the suffix filter with or without a prefix
- 909:exports a stored original to a new file with mode 0600, printing only size and digest
- 926:refuses an unknown digest, an existing file, a path that cannot be written and a bad digest shape
- 950:a user who is not an administrator gets the same answer as for a nonexistent original
- 972:publishes a copy of exactly the files pack.json lists, so a source directory may hold tests and a README
- 999:runs every check of publish and writes nothing: exit 0 when publish would succeed, 1 when it would refuse
- 1042:publish needs the publish right; then publish, list, assign, activate, unassign and retire
- 1092:text from a pack, a resource or an argument reaches the terminal with every control character shown as an escape
- 1126:add and list a resource, set its credentials from stdin (the password kept as its digest), read through it, disable and enable
- 1207:update changes metadata and addresses while merging settings by key, and refuses what makes no sense
- 1229:resource update origins prunes stale allow-plaintext entries with notification, clear-plaintext, and validates plaintext matching
- 1318:a credential set or a login reset clears the vault’s login state; a running Hub ends its stop at its next read, without a restart, and both commands say so
- 1382:without the vault master key the listings work, the login column says why, a reset works and secret set is refused before a value is read
- 1407:sync reports produced, skipped and failed builds and exits non-zero on a failure; a second sync produces nothing; roots and diff read the result
- 1484:doc put/list/show publish and replace a named document; only name, digest and size are printed; non-admins get the same answer as for a missing doc
- 1542:the help text lists the administrator commands
- 1551:R32 a fresh administrator command exposes noncanonical action diagnostics without body text
- 1562:R33 lease release honors the configured record byte limit atomically
- 1604:imports grouped credentials, hashes the password, shares a key across resources and supports dry run
- 1623:review 5: an invalid later resource value is refused before any credential is written
- 1633:missing and commented keys write nothing, malformed files disclose no values or paths
- 1656:authorization, vault and every target are checked before opening the environment file
- 1677:file key loss requires a fresh vault before reimport
- 1677:temporary keychain loss requires reissue and a fresh vault before reimport
- 1807:a read prints only declared names, outcome codes and artifact sizes and digests
- 1838:review 1: resource read exposes connector diagnostics only with the explicit events flag
- 1851:review 5: dry-run and real import validate every value before the first write
- 1875:review 6: missing and empty keys refuse explicitly with corrective hints and no writes
- 1891:review 8: Basic targets refuse before file access and a static authorization header reaches the stub
- 1943:pack publish forwards repeated explicit answer-request flags without touching the real keychain
- 1960:lease release refuses an unfinished dispatch job before changing the row
- 1977:lease ownership commands audit reassignment and list owner, holder and run without internal identities
- 2008:administrator sharing commands cover users package audiences policy listing and withdrawals
- 2038:pack shapes lists only unruled identities and refuses nonadministrators
hub/server/cli.test.ts
- 128:migrates as the owner role, grants the runtime role and exits 0; again it applies nothing
- 156:with a placeholder assembly module serve refuses with a message that says what is missing
- 163:serve refuses an operator token in its environment, by name and without its value, before anything starts
- 181:with an assembly it listens on loopback, answers /healthz and stops on the stop signal; the database keeps running
- 229:the first admin is bootstrapped with its token shown once; users and tokens are managed as the operator token’s admin; token values never listed
- 289:state-changing built-ins need an admin’s operator token: none, a user’s or an invalid one is refused before anything changes; CIRCLE_HUB_TOKEN works for one command
- 326:token issue –raw prints the token and a newline on stdout and nothing else; the confirmation goes to stderr
- 365:logs export and run show read as the operator token’s account, as the assembly’s read-only commands do: –as must name it; without a token –as or the only admin
- 423:stored text reaches the terminal with every control character as an escape: run show, user list, token list; JSON escapes what JSON.stringify leaves raw
- 490:logs export writes JSON lines without argument content, to stdout or to a new private file
- 521:run show and the reserved commands say clearly what a placeholder assembly module lacks; an unknown command is a usage error
- 544:registerCommand and the assembly hook add commands with records and the acting admin; built-in names are refused
- 574:run show reads through the module’s command-time assembly with the CLI’s stderr, never serve’s
hub/server/command-bridge.test.ts
- 66:a bridged command acts as the administrator of the given token, exactly as the CLI does
- 87:commands that are not bridged are refused before anything runs
- 97:a secret value reaches only the command readSecret, by its name
- 121:setup-code prints a code only while no administrator exists
hub/server/run-list.test.ts
- 149:pages newest first and the cursor continues without dropping a row
- 166:pack, user, state and since each keep only the matching runs
- 179:unknown keeps only runs whose newest accepted result has an unknown terminal
- 190:a non-admin is refused and the listing never prints submission text, quotes or the stored marker
hub/server/assemble/assemble.test.ts
- 77:a retired CIRCLE_SERVER_* variable refuses serve and the admin commands before the assembly or the key is touched
- 107:serve without a vault key stops before listening and never creates the key
- 120:serve with the platform keychain provider and no keychain item stops and never adds one
- 137:a vault that holds entries is never told to create a key: the restore advice replaces the provider’s key init
- 180:with a key, serve assembles the real packages, listens, serves the six tools to a token and stops
- 304:run show through the CLI uses the command-time assembly: it reads runs without the vault key, and serve’s assembly is never called
hub/server/assemble/cli-run-abandon.test.ts
- 17:run abandon has no anonymous fallback and permits only the authenticated runner or administrator
hub/server/assemble/cli-skill-migration.test.ts
- 18:skill bind-legacy resolves and displays a published digest and appends an authenticated migration declaration
- 38:skill bind-legacy appends migration declaration for xlsx@1 using workbook-format
- 65:skill bind-legacy appends migration declaration for xml@1 using xml-format
- 92:skill bind-legacy appends migration declaration for html@1 using html-format
hub/server/key-transfer.test.ts
- 14:records verify decrypts every vault entry and prints only the count
全部 9 项跳过
下列用例没有验证通过;不启用 Linux VM 或真实 systemd 来补跑。三个只输出 SKIP 的 Linux 用例,原因由各文件的 process.platform 条件核对。
- hub/platform/linux-lifecycle-real.test.ts:Linux synthetic deploy, backup, empty-root restore, verification and rollback use the real user manager;requires isolated Linux VM and HUB_TEST_SYSTEMD_REAL=1
- hub/platform/linux-restore.test.ts:Linux restore bootstraps an empty root directly from a verified synthetic backup;requires Linux PostgreSQL restore
- hub/platform/rehearse.test.ts:Linux rehearsal loses both private file keys and proves backups contain neither;skipped: Linux file key rehearsal
- hub/platform/systemd-real.test.ts:synthetic user unit really installs, stops, restarts and uninstalls on Linux;requires an isolated Linux user manager and explicit HUB_TEST_SYSTEMD_REAL=1
- hub/rules/sandbox/linux-isolation.test.ts:Linux OS isolation confines all Node threads and denies network, writes, unbound reads and bound executables;Linux-only operating-system isolation
- hub/rules/sandbox/orphan-linux.test.ts:Linux orphan cleanup kills only a matching installation marker;日志仅标记 SKIP;源码条件为非 Linux 时跳过
- hub/rules/sandbox/resident-linux.test.ts:Linux resident accounting finds a child started by a non-leader thread;日志仅标记 SKIP;源码条件为非 Linux 时跳过
- hub/rules/sandbox/resident-linux.test.ts:Linux resident accounting includes a supervisor and its child;日志仅标记 SKIP;源码条件为非 Linux 时跳过
- hub/rules/sandbox/sandbox.test.ts:without sandbox-exec the default refuses to run;sandbox-exec is available here
完整 unit 的执行命令:
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 npm run test:unit
本轮显式清空 UNIT_SHARD/JUNIT_REPORT,确保 all 覆盖完整 938 文件;acceptance 没有与 unit 并行。macOS 沙箱可用,相关正向用例实际执行。
完整 acceptance 首跑
在完整 unit 完全结束后,按并发 1 执行一次 npm run test:acceptance,236 项通过、0 失败、0 跳过、0 取消,退出 0,用时约 3,109.6 秒(51 分 50 秒)。以 Node 的最终汇总为计数依据,JUnit 同样没有 failure/error/skipped。场景 14、两个场景 28、26/41 的复核报告和全链评审,以及本轮场景 46~49 均实际执行;macOS 沙箱用例没有跳过。没有把本次通过当作之前间歇问题的根因证明,也没有更改其断言或添加重试。
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 npm run test:acceptance
本轮显式清空 ACCEPTANCE_SHARD,使用 JUnit 报告留存逐项结果;报告与原始日志留在本机临时目录,不入 Git。完整 unit/acceptance 都只执行这一次,后续修正仅定向复查。
完整 unit 失败的夹具修正
- 修改范围仅增加 hub/server/assemble/test-cli.ts、hub/server/cli.test.ts、hub/server/run-list.test.ts、hub/server/command-bridge.test.ts 的合成数据库准备:由测试数据库所有者显式收回 PUBLIC TEMPORARY,再调用原 CLI 迁移与授权。四处夹具共覆盖上述八个失败文件;生产的 grant/verify 拒绝条件与迁移 SQL 均不改,原业务断言均保留。
- 新增 CLI 回归,故意给运行角色 TEMPORARY 后要求迁移被拒绝,并证明 CLI 没有擅自收回这一权限来绕过拒绝。run-list 的前置迁移断言增加安全错误文本,避免只报 1 不等于 0。
- 定向复查包含全部八个失败文件、运行临时权限回归、迁移固定值,以及本阶段要求的 environment、diagnostics、PG 18 ownership 和 lock-namespaces;首轮 130 项中 126 通过、4 失败、0 跳过:命令桥接有独立建库入口,初次修正漏掉了它;补齐其默认/setup-code 两个数据库后,该文件 4 项全部通过、0 跳过。没有把首轮定向组写成全绿。原完整 unit 的 92 个失败现在均有定向通过证据,新增的 CLI 不安全权限拒收回归也通过;没有复跑完整 unit。
- 收尾时发现 Claude 新增的第四轮审查附录:审计在准入前写入的中等问题,以及 v1 暂不区分访问令牌作用范围的协调决定。本步先保留附录原文,审计修正和对应说明另作独立提交;不将其混入 fixture/验收提交,不提前声称该问题已经解决。
第 6 步收尾
- 定向合约/共享样例/场景 49 与四项指定基线组 33 项通过、0 失败、0 跳过。完整 unit 首跑和全部 92 个失败、9 个跳过如上逐项列出;完整 acceptance 一次运行 236 项全通过、0 跳过。夹具修正的定向首轮 126/4 与桥接最终 4/0 分开记录,不把全量 unit 改报为通过,也不叠加重复用例。
- 本步未改运行时业务代码、模型、生成物、迁移 14/15 或技能版本;完整验证暴露的变更仅为四处合成 CLI 数据库夹具与一项反向权限回归,业务断言没有放宽。最终类型/文档/差异检查在提交前执行。
- 不推送、不建 PR、不部署,不启动 Circle 仓库任务。第四轮审计准入问题另作提交;该审查附录保持未暂存供下一项原样收入。
npm run typecheck
npm run verify:contracts
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
contracts/src/sync.test.ts contracts/src/sync-merge.test.ts tests/acceptance/49-personal-sync.test.ts \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts hub/records/lock-namespaces.test.ts
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
hub/server/assemble/cli-admin.test.ts hub/server/cli.test.ts hub/server/command-bridge.test.ts hub/server/run-list.test.ts \
hub/server/assemble/assemble.test.ts hub/server/assemble/cli-run-abandon.test.ts hub/server/assemble/cli-skill-migration.test.ts hub/server/key-transfer.test.ts \
hub/records/personal-runtime-temp.test.ts hub/records/migrations.snapshot.test.ts \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts hub/records/lock-namespaces.test.ts
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" node --test --test-concurrency=1 --test-reporter=tap hub/server/command-bridge.test.ts
npm run test:docs
git diff --check
提交前收尾:npm run typecheck、176 文件合约核对和差异检查通过;最终文档检查 291 项通过、0 失败、0 跳过。四处 CLI 夹具与新权限拒收回归均已在上述定向运行中核对,原全量失败与全部跳过保持原始记录。
第四轮复核修正:先准入,再写同步审计
来源:Claude 在 kw-sync-review.md 新增的“第四轮复核(5509e847)”。先完成第 6 步并提交 b9bca19f,再独立处理本项;不重新运行两套全量检查。
做了的:
- 认证后立即按账户准入,之后才处理路径/方法和写任何同步审计。未知同步路径、错误方法同样计入账户频率,换路径不能绕过;只有已准入且路径/方法正确的请求能写开始/结束记录。未知路径 404、方法 405 和超额 429 不再追加记录,只使用原有的有界内存计数,不新增配置、持久化拒收日志或公开指标接口。
- 有效请求原有的审计开始失败则不操作、结束失败则回执恢复、body/response 全局预算、重认证与代次检查、清除事务和幂等处理保持有效。已准入的实际业务拒收仍可审计,受同一频率上限约束;不是删除所有失败审计。
- 回归使用每分钟 2 次的两个合成账户:一组先发未知路径与错误方法再发 40 次,要求审计 0 条;另一组先准入 2 次正常状态读取,再发 40 次混合请求,要求审计始终 4 条。所有超额请求均为 429,证明错误路径/方法占用频率且不能膨胀只追加记录。
- 按附录的低项协调决定,文档明确 v1 访问令牌不分路由作用范围:持有 MCP 访问 Bearer 的客户端同样能经预检/确认清除本人 Hub 内容,本机副本不受影响,可重新同步;v1 接受,作用范围留以后。没有新增 scope、MCP 工具、模型字段或数据库迁移。来源是 Claude 的协调决定,不冒充所有者原话。
- 更新 server/config README、CHANGELOG 与决定 138 的补充,第四轮审查附录原文收入本次独立提交。
验证:修正前真实回归失败 1 项,两个未准入请求写入 4 条,期待 0 条;失败不算通过。修正后定向结果另记。没有修改迁移 14/15 或重复全量 unit/acceptance。
本项最终验证:类型检查通过;修正后以下定向组 59 项通过、0 失败、0 跳过,包括所有同步 HTTP、安全拒收/审计故障、控制台本机方式与隐私、HTTP 关闭边界、方式事务/权限、迁移固定值和锁、environment、diagnostics、PG 18 ownership 与真实 Hub 场景 49。macOS 沙箱与 PG 18 备份/恢复实际执行。只审计已准入有效请求的上界回归通过,没有删除有效失败审计或放宽任何认证/读取规则。最终文档/合约/差异检查另在提交前执行,完整 unit 与 acceptance 未再次运行;此前首跑结果保持原始记录,不当作本次 HTTP 改动后的第二次全量验证。
npm run typecheck
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" HUB_UNIT_CONCURRENCY=4 node --test --test-concurrency=1 --test-reporter=tap \
hub/server/personal-sync.test.ts hub/server/console-sync-local.test.ts hub/server/console-sync-privacy.test.ts hub/server/http.test.ts \
hub/records/personal-sync-mode.test.ts hub/records/personal-runtime-temp.test.ts hub/records/migrations.snapshot.test.ts hub/records/lock-namespaces.test.ts \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts tests/acceptance/49-personal-sync.test.ts
npm run verify:contracts
npm run test:docs
git diff --check
提交前收尾:176 文件合约核对通过,文档检查 291 项通过、0 失败、0 跳过,git diff –check 通过。第四轮附录的原文已包含在本项提交;低项仅按 v1 协调决定作说明,没有偷偷增加令牌作用范围实现。
集成:变基到控制台视觉分支(Claude 2026-11-03)
来源:Claude 本轮要求按 #132 → #136 → sync 的顺序集成,保留旧分支、重新生成合约、采用 AppShell/Inspector/Dialog,并核对两主题及 1440/390 px。只在 kw-sync-local 本机处理,未推送、建 PR 或部署;主检出与 console-visual-local 的检出分支没有切换。
做了的:
- 将变基前 d25d2281 保留为 kw-sync-pre-visual;把原 4d2056c6 之后的 23 个提交重放到 console-visual-local 69c68216。变基后原第 6 步为 9f11afc6,审计准入修正为 8afd14bb。解决文档、知识首页、记忆表单、成员元数据和来源显示冲突时保留双方功能:新版框架、产品知识来源与待处理内容,以及本机方式的空视图、代次校验、原生表单与清除后的来源遮蔽。
- 从合并的 model 26 运行 gen:contracts;生成文件冲突不手工拼接。视觉分支已有 ui 字段与同步词表一并保留,已有清除来源标签仅补 ui 为“已清除的私人文件夹”,协议 label/代码不改。同步 HTTP、六工具、共享样例和技能镜像不变。migrations.ts 与 kw-sync-pre-visual 没有差异,14/15 的序号、名称与 SQL 原字节均保留,由原迁移快照回归核对,没有额外增加数据 SHA 扫描。
- 知识和记忆使用相同同步 Section、Badge、事实行与真实冲突 Stat;选择设置后由 AppShell 交给 Inspector,手机显示同一详情并保留返回入口。方式、状态与字段使用生成的词表;没有上次同步时间显示“—”,不把已提交一次与仍有活动会话混为同步结束。状态“等待同步中/等待清除中”仍按服务器返回;界面自身动作沿用交互脚本的“正在…”说明,不制造不存在的异步清除进度。
- 清除和重新启用使用 Dialog;预检、CSRF、代次、期望状态、幂等键、必选清除确认与原控制台 POST 保留。清除框正文说明本机副本、团队副本、运行独立持有原文与恢复备份的后果。成功后跳到同步设置,服务器会话给出一次性当前方式反馈;重放旧清除回执而当前已恢复 Hub 时,反馈当前方式,不声称又执行一次清除。
- 管理员成员详情在原 Inspector 中显示同步方式与格式化的上次同步时间,空间大小使用统一格式;不新增读取私人内容或冲突数量的权限。新版来源块在清除标记存在时隐藏旧私人树路径及技术引用,保留团队实际正文与引文。
- 新增 console-sync-design 回归,使用真实合成 Hub、密码会话与冻结双端样例:设置布局与用语、只读预检、未确认拒收、一次性清除反馈、重放旧回执时的当前方式、管理员元数据隐私,以及真实冲突计数和未解决记忆不加载。
验证过程(原始失败保留):
- 初轮同步/用语组 17 项中 16 通过、1 失败:改写确认说明时遗漏原“本机副本不受影响”的明确表述;补回该后果说明,原 HTTP 断言不变。
- 新 UI 回归首轮与同步组 16 项中 15 通过、1 失败:测试用全文查找判断成功提示是否存在,把固定脚本内的选择器当成了实际提示;改为只匹配实际渲染的提示元素。单独复现同一失败 1 项;没有把失败记成通过。
- 控制台组首轮 176 项,173 通过、3 失败、0 跳过。知识首页夹具未提供新增读取所需的 records 端口,补齐空端口;容量与清除来源的两个文本断言同步到统一格式/生成 ui 用语。原产品来源与待处理内容、私密信息不披露、清除后不出现旧路径等断言保留,不修改生产权限规则。
定向最终结果、验收 46~49、截图与跳过项目在以下收尾记录补齐。
定向检查与截图
- 最终定向组 119 项通过、0 失败、0 跳过。覆盖同步合约与规范形式、原生 CAS/双端冲突及原文重新验证、全部 personal 存储/清除/所有者函数/运行独立持有者回归、索引读取计划、HTTP 准入审计、三处失败文件及新 UI 回归;包括 environment、diagnostics、PG 18 ownership 的真实备份/恢复、锁命名空间和迁移固定值。类型检查与 176 文件合约核对通过。没有改变迁移 SQL,也没有给运行角色增加权限。
- 隔离 Chromium、临时 loopback 合成 Hub、临时 PostgreSQL 和独立浏览器资料,最终留存 9 个状态 × 两主题 × 1440/390 px = 36 张截图:同步设置、等待同步、已发生的上次同步(会话仍活动)、知识库冲突、记忆冲突、管理员成员详情、清除前确认、清除后当前方式、重新启用确认。最终集的每张原图均逐张打开核对,未只看缩略图;界面没有页面横向溢出,Dialog 与手机详情可读。清除框初轮复选项在按钮旁,移到正文后补拍;当前方式反馈调整后补拍清除结果和重新启用框,旧图不充当最终证据。
- 浏览器逐页检查渲染用语、页面宽度及脚本异常,最终 0 项用语问题、0 页面横向溢出、0 脚本异常。Dialog 的当前目标和焦点保持检查通过;额外实际验证必选确认未勾选时原生校验不通过,勾选后 FormData 带上确认值,点击按钮经增强的原控制台 POST 成功清除并回到当前方式。未调用 Bearer 同步 API 代替浏览器操作,也未接触运行中的 Hub 或个人浏览器资料。
- 截图及 JSON 报告、HTML 索引只留本机临时产物;最终截图目录的标识是 kw-sync-visual-final-nntinF(本地报告提供完整链接),不在 Git 写入本机绝对路径。临时 Hub、数据库、浏览器进程与资料已关闭/清理;截图保留供 Claude 审查。共享设计目录、设定、组件源文件及其摘要不改。
npm run gen:contracts
npm run typecheck
npm run verify:contracts
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" node --test --test-concurrency=1 --test-reporter=tap \
'contracts/src/sync*.test.ts' 'hub/records/personal*.test.ts' hub/records/read-plan.test.ts hub/records/migrations.snapshot.test.ts \
hub/knowledge/personal-sync/commit.test.ts hub/server/personal-sync.test.ts \
hub/server/console-sync-design.test.ts hub/server/console-sync-local.test.ts hub/server/console-sync-privacy.test.ts \
hub/server/console-knowledge.test.ts hub/server/console/member-space-metadata.test.ts hub/server/console/space-records.test.ts \
hub/platform/environment.test.ts hub/refs/diagnostics.test.ts hub/platform/ownership.test.ts hub/records/lock-namespaces.test.ts
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" node --test --test-concurrency=1 --test-reporter=tap \
'hub/server/console*.test.ts' 'hub/server/console/**/*.test.ts'
CIRCLE_HUB_LIBPQ_BIN="$HOME/.local/pgsql-18.6/bin" node --test --test-concurrency=1 --test-reporter=tap \
tests/acceptance/46-knowledge-spaces.test.ts tests/acceptance/47-knowledge-sharing.test.ts \
tests/acceptance/48-knowledge-console-memory.test.ts tests/acceptance/49-personal-sync.test.ts
npm run test:docs
git diff --check
未运行项:本轮依要求只做定向集成检查,不重新跑全量 unit/acceptance;前述完整首跑结果仍是变基前的证据,不冒充变基后的全量结果。没有修改来源工具或批准导入,不额外执行 verify:references/test:references。Linux 隔离/systemd 验证不在本机运行,不能算通过。此次实际运行组内的跳过数量以收尾汇总为准。
集成收尾结果
- 最终控制台组 176 项通过、0 失败、0 跳过,含新同步 Inspector/Dialog 回归、原 console.test.ts、只读页面/写表单、会话、CSP、SSE、用语、两主题设定与对比度,以及三处首跑失败的复查。首跑 173/3 保留在上文,不把两轮相加。
- 随后串行执行验收 46~49,36 项通过、0 失败、0 跳过,约 63.1 秒。macOS 沙箱相关用例实际运行;未改验收断言。没有与另一套重型测试组并行。
- 最终类型检查、176 文件合约核对、文档检查 314 项通过、0 失败、0 跳过,git diff –check 通过。PG 18 ownership 及锁检查已包含在前述 119 项定向组,不重复累计。
- 最后对照组件规范补齐同步 Inspector 的类型/状态、标题、事实和底部操作结构;知识和记忆仍共用同一设置。完整矩阵补拍后再次逐张查看 36 张,确认操作栏沿用既有底部规则。Dialog 采用实际视口截图,避免整页截图把固定遮罩之外的虚拟扩展区误当作页面缺陷;没有改共享 CSS、设定或组件。最终目录标识与上文一致,JSON 报告和 HTML 索引已更新。
- 本轮所有实际运行组均 0 跳过;全量 unit/acceptance、未变更的 reference 工具组与 Linux 专属作业依本轮范围未运行,不计作通过。变基前的 236 项完整 acceptance 证据仍仅对应旧分支。