circle-server 第二阶段实施简报:bug 闭环

历史文档(2026-10-21 标注):本文是已退役的任务与审批服务器第二阶段的简报,已被 Hub r4 简报取代,只作记录保留,不是现行指令。

2026-10-12:暂停执行,等待新简报。 第一目标已改为以知识库为核心,将现有产品问答、用例编译、用例评审能力通过 Circle 和 circle-server 接起来。本文保留此前的方案和脱敏来源结构,不能据此继续禅道或构建站点桩。当前已完成与未完成项以 server-s2-progress.md 的暂停收尾记录为准。

2026-10-12。用户和 Claude 讨论后确认了第二版方案。方案页面是私有的 claude.ai 页面,你打不开,本文件是它的可执行摘要。第一阶段在 server-s1(PR #5,尚未合并),本阶段从 server-s1 的最新提交开始。Circle 侧的命令和页面由 Claude 在 Circle 仓库完成,你不要改 Circle 仓库。

同日范围更新:缺陷来源改为禅道数据的 PLM 查询镜像,HTTP JSON 只读,使用每个用户自己的 SSO JWT。Bugzilla 退出第二阶段,取消双缺陷系统与 HTML 桩方案。已获得脱敏只读探测结构,当前据此实现合成 JSON 桩与映射。服务地址和鉴权值均不登记进仓库。

1. 授权范围

用户 2026-10-12 授权:

  • 在新的隔离 worktree 和分支上继续用 TypeScript 实现 server。
  • 安装依赖,版本写精确值,并提交锁文件。
  • 在本机启动 PostgreSQL、server 和 worker 进程,只用合成数据、临时目录和临时数据库。
  • 只读参考本机 SyntheticReference 仓库(../synthetic-reference-engine)里的对象抓取、失败归因、缓存与命令树读取行为。

仍需先问用户:push、开 PR、改 GitHub 设置或发布流程、任何部署。

不允许:

  • 调用付费模型或真实模型,一律用脚本模型。
  • 连接真实设备或真实企业系统。后续来源与 Git、CI、OA、企业微信均只用获准合成桩;当前不编写缺陷来源桩。
  • 运行、import 或复制 SyntheticReference 的 Python 代码,把它变成依赖;只能读源码,用 TypeScript 重写行为。
  • 把 SyntheticReference 里的主机名、账号、Cookie、真实缺陷内容或配置值写进本仓库。
  • 读取真实 Circle 用户数据;修改 references/ 或 foundation/circle 的字节;从生产入口 import 归档模块。

仓库已经公开。 提交里不能出现凭据、内部地址、真实业务数据、本机绝对路径或对话记录。新文档里引用本机其他仓库时用相对路径。

第一笔提交:更新 AGENTS.md 的 Current scope,写明这次授权和上面的边界,并登记本文件。

2. 定位:第二版

第一阶段证明了机制,但 server 只会执行写死在核心里的三个能力包。第二版把 server 做成企业通用平台:

  • 对象:企业里的名词(人、缺陷、需求、代码变更、构建、版本、型号、设备等)从各系统映射过来,带来源和修订,彼此关联。B 负责。
  • 五种动作:对接(连接器)、阅读(跨系统查询和关联,带出处)、操作(D,第一阶段已有的操作账本、租约、审批、回读、核对)、订阅(系统事件或定时,C 负责)、方法(把输入变成产物,E 负责)。每个用户请求都是它们的组合。
  • 固化阶梯:对话 → 留痕 → 个人 bot → 团队工作流 → 模板。同一份能力定义沿阶梯上升,每升一级加一层治理。定义由人在本地用 Circle 写出并试跑,交给 server 审批,批准后成为 server 的能力。
  • 核心不认识任何具体系统、产品或流程。 公司和产品线特有的东西只放在四类包里:连接器、对象映射、方法、策略。新增产品线是加映射和方法,换缺陷系统是换连接器,另一家公司是配连接器、映射和策略。

server 本身没有界面。查看和维护都在 Circle:终端里是 /server 命令,桌面里是按权限出现的页面,二者调同一套 API。不做独立 Web 管理站,不另做控制台应用。以后的报表页只读,打开的人登录后按自己的权限出数,本阶段不做。

3. 这一阶段交付什么

第一个切片是 bug 闭环:回答 SE 的三个问题(这个缺陷修了吗、在哪个版本修的、哪些型号能用),并支持一个个人订阅(发版本时通知我)。缺陷来源为禅道数据的 PLM 查询镜像;第 1 幕验证单系统词表映射,通用对象身份仍按来源系统、来源类型、外部编号区分。

合成场景(全部虚构):

  • 产品线 LBX,型号 LBX-100、LBX-200、LBX-300。
  • PLM 镜像桩含完全虚构的不同项目缺陷与受限记录,按已提供探测结构实现;未确认的权限/缺失码不猜测,空数组不当作确认不存在。
  • Git 桩:提交信息里引用缺陷编号,有合进主线的,也有只在分支上的。
  • 构建站点桩:每个产品、平台、构建的目录,版本由哪个构建发布,构建包含哪些提交。平台就是 SE 说的型号(待用户确认)。
  • 命令树 XML 桩:每个产品、平台、构建一份,元素结构参照 SyntheticReference 读取的命令树,内容全部虚构。用它回答某条命令在某个平台的某个构建上是否存在。
  • OA 通讯录桩和身份映射:同一个人在缺陷来源、Git、OA 中的账号不同;PLM 调用使用本人的 SSO JWT。
  • 企业微信桩:只记录收到的通知。

验收按幕组织,每一幕是 tests/acceptance 里的一组自动 HTTP 测试:

幕 必须证明
1 映射 PLM 镜像的状态、resolution、严重程度词表映射到平台词表;未知值明确披露为未映射,不默认归类。已解决但 resolution 不表示修复时不能判已修复;缺陷与需求的同号记录仍是不同对象
2 关联 主发布链为禅道详情 bugzillaID → 构建站点 Bugs_Fixed 清单编号 → 构建名与 Released on 日期;不连接 Bugzilla。连接键为空明确缺证据;多构建命中取最早正式发布并列出全部;只有 Beta 命中不算正式发布。命令树 XML 与发布记录同站点、按构建/平台引用。resolvedBuild 与镜像构建 bugs 仅作补充构建线索,收集桶不能当发布版本;提交和型号等其他环节缺失继续披露
3 权限 请求人看不到的缺陷,任何查询都不泄露,包括计数、标题和关联出的型号。权限取请求人在源系统的权限和平台策略的交集。连接器内部区分不存在、没权限、登录失效、源系统不通,只有确认不存在才缓存;对请求人一律只说看不到
4 身份 「分给我的」按平台 person、来源账号与本人 SSO 身份映射查找;映射缺失明确报错,不按姓名匹配,不借用服务账号扩大可见范围
5 订阅 个人订阅「LBX 发版本时通知我」:用订阅人的身份运行,版本发布事件按外部 id 去重,只通知一次;订阅人无权看到的版本不通知;server 重启后不丢不重
6 固化 第一阶段的 defect-review 改写成能力定义,经提交、机械核对、审批、影子运行、转正;PLM 来源始终只读,影子运行记录计划产物/通知,不对来源评论或改状态;改变输出/动作范围后重新审批,旧批准不能用于新摘要
7 通用 核心代码(apps/、packages/ 中除连接器、映射、方法包以外的部分)里搜不到具体系统、产品或能力名:bugzilla、zentao、禅道、lbx、mindmap、defect-review 等。用一个测试固定下来

第一阶段的 40 项测试继续通过。其中依赖 lbx-healthcheck 的机制测试(租约过期、fence、审批绑定、事实交付)改用一个中性的合成设备操作,保证机制仍被测到。脑图编译保持现有行为和测试,但从核心移到方法包,通过注册进入能力目录。

4. 关键设计

对象和关联(B)

  • 通用的实体表和关联表:实体有 tenant、scope、类型、来源系统、外部 id、修订、属性和访问控制;关联有两端、类型、来源、修订和观察时间。
  • 对象类型词表本身是一个带版本的包(第一版:person、defect、requirement、change、build、version、model、feature)。核心只实现存储、遍历、权限过滤和出处,不写死这些类型的业务含义。
  • 读取时按请求人过滤。关联的另一端看不到时,这条关联对请求人也不存在。
  • 每次从源系统读入都是一次观察,保留原始返回的 SHA-256 作为证据,沿用第一阶段的 B。

连接器规范 v1

第一阶段的 ConnectorManifest 只有 defects、documents、product 三种角色,清单写死在代码里,没有注册接口和凭据存放。v1 改为:

  • 声明 objects(提供哪些对象类型,各用哪个映射)、read、act(每个写动作的 effects、幂等方式、核对方式,沿用第一阶段)、observe(事件、游标或 webhook)、identity(来源鉴权方式,以及用户账号如何对应到平台的人)。PLM 来源没有写动作,鉴权绑定请求人自己的 JWT。
  • 连接器和映射作为包注册,有版本;由管理 API 启用到某个 scope。注册和启用都写审计。
  • 凭据存 server 端,只以引用出现在连接器配置里,只注入 worker 的执行环境,不出现在日志、事件和 API 返回里。
  • 原计划的注册接口应支持本人运行时注册个人工作台 SSO token,并将“某人的凭据”绑定成某连接器的凭据引用;默认按所有者隔离,普通用户不能借用他人引用。来源值不回显,未来改服务 token 时另行治理。该接口尚未实现,当前暂停。
  • 连接器在 worker 里运行,每次外部调用沿用第一阶段的路径:身份、权限、动作声明、操作账本、执行、留证。

身份映射

  • person 是一种对象,挂着各系统账号。来源可以是管理员维护,也可以是 OA 通讯录同步。
  • 映射有修订和审计。缺映射时报 IDENTITY_UNMAPPED,不做模糊匹配。

阅读接口

  • 提供通用的对象读取和关联遍历接口,返回结果和出处链,以及没走通的那一环。
  • SE 的三个问题写成 E 里的确定性方法(例如 defect-fixed@1、fixed-in-version@1、applicable-models@1),在对象图上运行,产出脚本结论。核心不包含这些问题的专用代码。
  • 写一份客户端交接文档,和第一阶段的 client-api-handoff.md 同样详细:路由、schema、权限、错误、分页、出处格式。Claude 据此做 Circle 的命令和页面。

订阅和个人 bot

  • 订阅属于一个人,用本人身份运行,只能读本人能读的对象,不需要组织审批。
  • 订阅的触发沿用第一阶段的游标和外部事件去重;通知走企业微信桩,同时在 Circle 可读的事件流里出现。
  • 订阅可以列出、暂停、删除。

能力定义 v0

  • 格式按方案页面的草案:id、version、owner、trigger、reads、acts、steps(script、model、human 三类)、produces、deadline、escalate。声明里只写对象和动作,不写具体系统。
  • 提交时打成按内容寻址的包,附试跑记录。server 做机械核对:试跑里有没有用到声明之外的动作,脚本的测试在沙箱里能否重跑通过。
  • 审批沿用第一阶段的审批:绑定摘要,申请人不能自批;审批人由声明碰到的对象和动作决定。
  • 影子运行:在真实事件上跑,写动作只记录不执行,结果可对比。转正后用服务身份,权限等于声明的范围。在途实例钉住启动时的版本。
  • 模型步骤本阶段只用脚本模型,产出标为模型判断。

从核心搬出去的

以下位置写死了具体能力或产品,本阶段全部搬到包里或改成通用机制:

  • packages/contracts/src/index.ts:PACK_FLOWS、提交接口的能力包枚举、MindmapPack、DefectReviewPack、LbxPack、RuleRequest 里的 defect-review、ConclusionRequest.methodId 里的 lbx-healthcheck、按能力命名的 ChecklistPredicate。
  • apps/server/src/main.ts:静态能力包列表、按 mindmap-compile 和 defect-review 分支的提交逻辑。
  • apps/server/src/mindmap-runner.ts、professional-runner.ts:按包名查询和分支。
  • packages/tasks/src/rules.ts、checklists.ts,packages/methods/src/assessments.ts,packages/tasks/src/index.ts 里按包名或方法名的判断。
  • packages/execution 和 worker 里对 mindmaps.compile 和 LBX-LAB-01 的特殊处理。
  • packages/connectors/src/index.ts 里硬编码的清单。

交付清单的谓词改成通用形式,例如「某方法的某版本对某次观察给出了 supports」,不按方法名判断。

5. PLM 只读查询与参考边界

SyntheticReference 在 ../synthetic-reference-engine。只读源码,不运行,不复制代码和配置值。主要文件:

  • main/ingest/_capture_session.py:登录和会话保存。
  • main/ingest/defect_fetch.py:逐条抓取、失败原因、原始页面存储。
  • main/ingest/html_extractors/:仅参考字段提取失败、身份核对与规范化的边界,不采用其页面接入方式。
  • main/ingest/defect_parse.py、main/ist_core/tools/knowledge/kb_bug_search.py:解析、按编号查找、不存在缓存。
  • main/sync/command_tree_sync.py、scripts/maintenance/build_vendor_stdlib.py:命令树 XML 的来源和结构。

旧参考是浏览器逐条抓取,当前来源是同步镜像的 HTTP JSON。旧代码不作为接入实现,也不据它猜测镜像字段、分页或鉴权错误。

已确认的相对接口例子:

  • GET /plmquery/bug/
  • POST /plmquery/bug/detail/
  • GET /plmquery/build/
  • GET /plmquery/build/{id}/
  • GET /plmquery/product/all/

鉴权为请求人自己的 SSO JWT,直接放在 Authorization 头中,不加 Bearer 前缀。它与平台本身的 Bearer token 分开存储和使用;来源 JWT 只通过服务端秘密引用交给获准 worker,不能出现在配置返回、日志或事件中。

已知服务没有登录接口,JWT 由工作台 SSO 签发,有效期约 30 天,claims 含 username、userId 等。HTTP 成功与失败均为 200,以 JSON code 判断:1000 成功;401 的已知 msg 为「Token不存在.」「Token解码失败.」。成功数组的承载字段、其他失败码和 resolution 完整词表仍需确认,不按 HTTP 200 或空结果推断已授权/不存在。

bug 列表用 product、status、severity、assignedTo、openedBy、lastEditedDate_start/end 等 query 过滤,不分页,page 无效。列表数字为数值,日期为带 T 的无时区 ISO;详情批量 body 为 {bug_ids:[...]},最多 100 项,数字多为字符串、日期以空格分隔。两者日期都按 +08:00 规范化;空串和零日期缺省值保留为缺失,不造时间。详情含 openedBuild、resolvedBuild、resolution、外部历史编号、代码位置与 actions 历史;assignedTo 可能是 closed 状态值,不能当人名。

严重程度 1/2/3/4/5 为致命/严重/一般/轻微/建议;优先级 1/2/3/4 为立即/高/正常/低;状态为 active/resolved/closed。未知值列为未映射。增量按 lastEditedDate_start 轮询,详情按 (id,lastEditedDate) 去重;考虑镜像时间略超前及查询重叠,不简单用本机当前时间跳过记录。

构建列表含 id/product/branch/project/name/scmPath/filePath/date/builder/desc,不含 bugs;详情增加 stories、bugs(逗号 ID)、deleted、mailto。用 resolvedBuild 查详情、bugs 反查关联时只确认构建线索,保留发布环节缺失。全字段与验收来源见第二阶段进度。

发布主证据来自独立构建站点的 HTML:首页链接版本线页,也有 kvm/docker/xen/vmware/hyperv 和系统/芯片变体页。版本线页每构建含 Rel_/Beta_ 构建名、构建时间、Released on 日期和四个链接:系统镜像 .demoimg、SNMP MIB、command_tree-构建名.xml、Bugs_Fixed_with_构建名.html。Bugs_Fixed 每行的 show_bug.cgi?id 链接只用来抽取历史编号;用 bugzillaID 连接,不打开链接目标。命令树 XML 同构建/平台保留原件和 SHA。桩的全部编号、名称、日期、页面和 XML 都虚构。

本阶段 circle-server 先在用户 Mac 上、本机监听运行;网络开放、远端部署或连接真实来源均未获本轮授权。

保留的资料边界:

  • 产品 XML 只有命令树:每个产品、平台、构建一份,结构是 commands → scope → menu → item,item 下有 arguments/arg 和 results/result。用来判断某条命令在某个构建上是否存在、参数怎么取值。没有型号或功能矩阵的 XML。
  • 文档库是 WebDAV,OpenKM 的支持已经去掉。没有 OA 和 CMDB 对接。

要保留的做法:

  • 失败原因使用封闭集合,区分确认不存在、没权限、登录失效、元数据不全、传输失败。镜像的具体判断依据等待探测结果,不把页面文字规则迁成 JSON 规则。保存前核对返回对象编号与请求的一致。
  • 只有确认不存在才缓存;没权限、登录失效和传输失败都不负缓存。
  • 同一个编号可能对应不同对象类型,身份始终包含系统、类型、编号。
  • 原始接口返回按 SHA-256 保存,作为证据,和第一阶段的 B 一致。

不要照搬的:

  • 门户登录、浏览器抓取、把本人鉴权换成更宽的服务账号,或绕过登录/权限检查。
  • 只看状态不看 resolution;把指派人当解决人、最后修改时间当解决时间;未经实际实例确认严重程度数字方向;用未经核验的繁简转换改写字段。
  • 不存在缓存永不过期,正缓存永不刷新,每抓一条就重新解析整个目录。
  • 企业微信机器人遇到新用户自动建成管理员。平台第一次见到一个人时不给任何角色,权限只来自身份映射和策略。

已完成通用对象存储切片;当前按脱敏结构实现 PLM HTTP JSON 桩、无分页列表与字段映射,不新建 Bugzilla 桩或 HTML 来源桩。第一阶段继承的 Bugzilla 桩将在兼容迁移中移除,其账本、游标、幂等与核对测试改用中性合成资源。

仍待确认的内容:成功信封的数组字段、resolution 全词表、权限/确认缺失/故障的区别;提交信息引用规则、构建包含提交的来源、发布记录与平台/型号关系、企业微信通知方式。已知列表无分页、详情上限 100、鉴权 JSON code 和严重程度方向不再列为待定。

6. 构建顺序

  1. 第一笔提交:AGENTS.md 范围和本文件。
  2. 通用核心测试(第 7 幕)先写,确认它现在失败,列出要搬的位置。
  3. 对象和关联存储、对象类型词表包、权限过滤。
  4. 连接器规范 v1、注册和启用、每用户凭据引用;按当前脱敏结构实现 PLM HTTP JSON 桩及映射;Git、构建站点、命令树 XML、OA 通讯录、企业微信桩。对应第 1、3、4 幕,未确认字段不得猜测填充。
  5. 阅读接口和三个方法,对应第 2 幕。写客户端交接文档。
  6. 订阅,对应第 5 幕。
  7. 能力定义 v0、机械核对、审批、影子运行和转正,把 defect-review 迁出核心,对应第 6 幕。
  8. 把脑图编译移到方法包,撤下 lbx-healthcheck,机制测试改用中性合成设备操作。第一阶段的测试全部通过。
  9. 第 7 幕通过,npm run dev:stack 能启动包含新桩的合成环境,打印 Claude 接 Circle 时需要的地址、账号和示例。

7. 工作方式

  • 先读 AGENTS.md、README.md、docs/bcde-integration-plan.md、第一阶段的 docs/server-build-brief.md、docs/server-s1-progress.md 和本文件。
  • 建 worktree:git worktree add ../circle-server-s2 -b server-s2 server-s1。把本文件(主工作树里未跟踪的 docs/server-s2-brief.md)放进你的分支提交,然后删掉主工作树里这份未跟踪的副本。
  • 显式写出要提交的路径,不用 git add -A、git add . 或 git commit -a。提交信息按 AGENTS.md:一句首字母大写的英文祈使句,不加前缀,不加句号。
  • 每一幕先写验收测试并确认失败,再实现。npm run verify:references 和 npm run test:references 保持通过。
  • 记下实际跑过的命令和结果,不宣称没跑过的检查。每完成一步,在 docs/server-s2-progress.md 更新:做了什么、跑了什么、还缺什么。
  • PR #5 如果先合并,把 server-s2 rebase 到新的 main 上再继续。
  • 遇到需要用户决定的事就停下来问,例如要 push、要扩大范围、合成桩无法表达 SyntheticReference 里看到的某种真实行为。