Skip to content

Keep PR review collaboration alive beyond author sessions - #573

Draft
baicaicc wants to merge 1 commit into
masterfrom
feat/pr-review-room
Draft

Keep PR review collaboration alive beyond author sessions#573
baicaicc wants to merge 1 commit into
masterfrom
feat/pr-review-room

Conversation

@baicaicc

Copy link
Copy Markdown
Collaborator

What changed

  • Add botmux pr-room open|adopt|repair|finish|list for a durable PR-scoped Feishu review group.
  • Reuse Botmux federation to invite author/reviewer agents and their human Owners.
  • Add authenticated remote reviewer preparation so cross-deployment agents trust the group before kickoff.
  • Persist structured reviewer/workdir/Owner setup intent with atomic repair claims and finish-safe lifecycle transitions.
  • Prevent duplicate groups after ambiguous or crash-interrupted creation; require explicit adopt or finish + --reopen reconciliation.
  • Document the workflow and its current distributed-uniqueness boundary.

Why

PR review handoffs currently depend on an ad-hoc group and a live author session. If the author session closes or the Owner creates a separate group, agent context and follow-up can be lost. PR Rooms make the collaboration durable and recoverable until merge, close, or explicit abandonment.

Safety and concurrency

  • PR URL (including SCM host port) is the lifecycle key.
  • Concurrent setup repair uses a single attempt token, so kickoff is not duplicated.
  • finish waits for a live setup attempt, then resolves atomically.
  • Expired creation claims become indeterminate instead of automatically creating another group.
  • A room is never marked ready while setup has an error or unresolved Owner/reviewer/workdir state.

Validation

  • pnpm build
  • tsc --noEmit
  • 63 targeted unit tests
  • Full unit suite: feature tests pass; the same 15 baseline environment failures remain (node:sqlite, fake Codex ESM fixture, Linux process scan/timeouts, hook/MCP sandbox environment)
  • git diff --check
  • Independent code review: APPROVE
  • Independent architecture/concurrency review: CLEAR

Known boundary

Cross-deployment simultaneous open calls do not yet share a distributed uniqueness lock. The documented contract is to invoke open from the PR author's hub/main deployment.

PR handoffs previously depended on ad-hoc chats and a live author session. Add an idempotent PR Room lifecycle that creates or adopts a Feishu group, prepares local and federated reviewer agents, persists setup intent, and lets the author repair or finish the room safely.

Constraint: Feishu bot open_id values are app-scoped, so remote reviewer readiness must be acknowledged by the owning deployment before kickoff.

Constraint: Cross-deployment simultaneous open calls do not yet share a distributed uniqueness lock.

Rejected: Retry expired or timed-out group creation automatically | could create duplicate review groups after an ambiguous remote success.

Confidence: high

Scope-risk: moderate

Reversibility: clean

Directive: Preserve the setup attempt token and finish-pending invariants before adding new PR Room side effects.

Tested: pnpm build; tsc --noEmit; 63 targeted unit tests; full unit suite with only 15 baseline environment failures; dual code and architecture review.

Not-tested: Live cross-deployment Feishu group creation against two production Botmux deployments.
@baicaicc
baicaicc marked this pull request as ready for review July 23, 2026 13:22
@baicaicc
baicaicc requested a review from deepcoldy as a code owner July 23, 2026 13:22

Copy link
Copy Markdown
Collaborator Author

@deepcoldy 已完成实现并请求 Code Owner review。验证包括 pnpm build、63 项定向测试、完整 suite 基线对比,以及独立 code/architecture 双审。

我也用本分支实际创建了对应的 Botmux PR Room,双方 agent 已入群、作者 worktree 已绑定。由于你的部署尚未包含本 PR 新增的 /api/federation/delegate-prepare-review,首次自动 reviewer kickoff 按设计落为 active/degraded,未误报成功;我已在群里写明自举状态。合入并升级后执行 botmux pr-room repair https://github.com/deepcoldy/botmux/pull/573 即可完成远端 trust 与 kickoff。

@baicaicc
baicaicc marked this pull request as draft July 23, 2026 13:30

Copy link
Copy Markdown
Collaborator Author

将此 PR 退回 Draft,暂停实现推进与合入。

原因:这个能力涉及 Botmux 的群组、federation、session、权限信任、调度和生命周期边界,应该先完成对既有能力与架构的系统盘点,再决定是否需要新增 pr-room 概念、应该落在哪一层,以及最小兼容演进路径。当前代码保留为 design spike / 可执行原型,暂不作为已批准方案。后续会先提交架构分析、备选方案与明确的 acceptance criteria,再决定重构、缩小或废弃当前实现。

@deepcoldy deepcoldy left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: PR Review Room 生命周期 + 联邦建群 + 原子 claim 并发

审查方式:钉 PR head f208d017 隔离 checkout(pnpm install --frozen-lockfile + pnpm build),跑 PR 全部新增/改动测试 + 全量 unit/e2e,对当前 master 做 trial-merge 与回归对拍,并对状态机 / 失败回滚 / 联邦端点做了对抗式并发分析(含对纯函数与错误分类逻辑的实机复现)。

✅ 验证通过的部分

  • 构建/类型pnpm build 绿、tsc 干净。
  • 测试:PR 自带 4 个测试文件共 56 例全过(pr-room / webhook-lifecycle-store / cli-arg-utils / federation-api)。全量套件里 21 个失败文件中,18 个是 e2e/browser(需 live 飞书/浏览器/CLI),3 个非 e2e(scheduler / schedule-card-model / v3-distillation-runner)在干净 master 上同样失败——本机时间敏感基线,对 master 零回归
  • 共享 store 影响面webhook-lifecycle-store 是既有 webhook-routes.ts 的公共依赖。改动用重载保留了 2 参旧签名,旧 caller(opts={})走的 blockResolvedReopen/blockIndeterminateRetry/adoptIndeterminate 全 falsy,行为与 master 逐字一致;新增的 beginWebhookLifecycleSetupRepair/complete/isCurrent/markIndeterminate/resolve 的新语义 pr-room.ts 消费,隔离干净。
  • 合并:对当前 master 仅 README.md机械冲突(master 重写了开头段落,PR 的 pr-room 文档块本身干净保留),src/cli.ts 自动合并。package.jsonbuild 脚本看似删了 generate-runtime-build-id.mjs——实为分支老(229 commits 前),该步是分支点后才加到 master 的,3-way merge 会保留 master 版本,非回归。需 rebase。
  • 状态机核心creating/active/resolved + setup 子状态 + setupRepairId claim(30min TTL)设计扎实。双重 kickoff、kickoff-after-resolve、并发 repair 都被「发送前紧邻 assertSetupRepairCurrent + busy/setupReviewersReady 闸门」正确围栏。联邦 delegate-prepare-review 端点鉴权在副作用前、reviewer 强制 local + in-chat 校验、teamId 取自鉴权 membership(不可伪造)、幂等 key 与 3 个姊妹端点同构,安全。纯函数 parsePullRequestRef 边界(10 组输入实测)、argValuesresolveRosterBotRef 均正确。

🟠 建议修复(P2,非合并硬阻塞,但都属「本 PR 明确想防的失败模式」)

P2-1 · openPrRoom setup 阶段抛异常会把 repairing claim 悬挂 30 分钟(与 adopt/repair 不对称)
src/cli/pr-room.ts:405-406(workdir 分支)与 :424(prepare 前)的 assertSetupRepairCurrent / bindOncall任何 try/catch 之外,且 openPrRoom 无 try/finally 保证 completeWebhookLifecycleSetupRepair(:499)一定跑到。而 bindOncall预期失败返回 {ok:false}(被当 degraded 处理),但底层 rmwBotEntrywithFileLock(bots.json) 在锁竞争超时(MAX_WAIT_MS=5000)或 fs I/O 错误时抛异常——oncall-store 头注释明确写了「12 daemon + 1 dashboard 抢 bots.json、并发 auto-bind 突发」,5s 锁超时抛错是现实可达的。异常逃逸到 cmdPrRoom 顶层 catch(:993)只 console.error+exitCode=1从不释放 claim

后果(30 分钟窗口内):repair 命中 busy 闸门 → 拒绝执行;open 复用后仍提示「去 repair」→ 死循环;finishwaitsForSetup=truependingResolved 但 setup 永不完成 → 房间无法关闭。只能等 30min TTL 自愈。adoptPrRoom(:648-696 每段 try + 无条件 complete)和 repairPrRoom(:catch 里必调 complete)都没这问题。

修法:把 open 的 :401-498 setup 区包进 try/catch(或 try/finally),异常路由到 degradedReasons 并保证 completeWebhookLifecycleSetupRepair(error=...) 一定执行,与 repair 的结构对齐。

P2-2 · 网关 5xx 被误判为「确定未建群」→ 重复建群风险
src/cli/pr-room.ts:213-221 + isAmbiguousTransportError(:155-162)。pr-room open 走本地 createLocalTeamGrouporchestrateFederatedGroup 在此路径永不返回 408/504(那是 no_online_daemon 联邦委派分支才有),所以 isAmbiguousGroupCreateFailurestatus===408||504 检查在本路径是死代码,歧义判定 100% 落在 isAmbiguousTransportError 对抛出错误的匹配上。

实测(复刻该函数逐一喂错误形状):Lark SDK 是 axios,HTTP ≥400 会 throw e(原始 AxiosError)。网关/反代在请求已转发给 Lark 之后返回的 504/502,形状是 code:'ERR_BAD_RESPONSE' + message "Request failed with status code 504"——isAmbiguousTransportError 判定为 definite → 走 failWebhookLifecycleGroup 删掉 creating 记录 → 下次 open 重新建群 = 重复群。而 socket hang up / ECONNRESET / ETIMEDOUT / AbortError 这些被正确判 ambiguous 的,never-connected(ECONNREFUSED/ENOTFOUND)判 definite 也对。缺口只在「请求已发出、响应层 5xx」这一类。可达性取决于出网是否经反代,SUSPECTED-medium。

修法createGroupWithBots 一旦请求发出后抛的任何异常都应视为 ambiguous(建群与否不可知),只有 pre-flight / 从未发出的错误(连接被拒、DNS)才判 definite。或在 createLocalTeamGroup 的 catch 里对 AxiosError 带 response.status>=500(甚至任何 response 存在)显式归 group_create_indeterminate:

🔵 次要观察(可选,非阻塞)

  • resolveReviewerMentionOpenIds(:131-144)单遍贪心 + 共享 used 对同 display-name 的 reviewer 有次序依赖——但实测证实无实际危害:真正在群里、可唯一解析的 bot 永远会进 openIds(被 ping 到),次序只影响 missing 诊断标签在同名项间的归属(cosmetic)。想更干净可两遍解析(先绑 exact larkAppId,再 display-name)。
  • delegate-prepare-review 的信任门 isTeamGroupChat 跨 team 匹配 chatId(忽略 teamId),即一个 team 下记录的绑定会全局授予 bot-to-bot 信任——在既有联邦信任范围内且是端点本意,仅提示。
  • adopt 路径若 reviewer 的远端部署在 roster 构建与 prepare 之间离开联邦(TOCTOU),该 reviewer 既不进 ready 也不进 failed,adopt 仍会 @ 它(依赖后续周期同步兜底)——窄且是「欠授信」非「越权」。
  • adoptPrRoombegun.action === 'indeterminate' 分支是死代码(adopt 传 adoptIndeterminate:true 只会得到 reconcile),可清理。

结论

核心并发设计正确、隔离干净、零回归、测试充分——工程质量高。两个 P2 都命中了「本 PR 明确要防的失败模式」(悬挂 claim / 重复群),建议合并前修掉,尤其 P2-1(多 daemon 抢 bots.json 时现实可达)。另需 rebase 解 README 机械冲突。

审查覆盖:build/tsc/56 新测试/全量套件回归对拍/trial-merge/状态机 & 失败回滚 & 联邦端点对抗式分析 + 纯函数与错误分类实机复现。fork PR 无 CI,以上均为本地隔离验证。

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.

2 participants