自托管 GitHub 三审修复机器人 robomp:以 omp RPC 为内核的 Issue 自动化处理架构与实践指南
【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi
本指南以 oh-my-pi 仓库中 python/robomp/AGENTS.md 为主体,结合 robomp 的源码、配置、容器编排与测试,系统讲解这个自托管 GitHub 分流修复机器人(triage-and-fix bot)的完整架构:它如何把omp --mode rpc作为子进程驱动,如何从 Webhook 到修复 PR 走完一条可靠、可恢复、可审计的数据流,以及开发者如何在本仓库中配置、运行、测试与扩展它。读完本文,你将掌握 robomp 的事件路由、会话续跑、工作区沙箱、双容器信任边界与全部ROBOMP_*配置项,并能在自己的仓库中复刻这套"Issue 进来、PR 出去"的自动化闭环。
项目定位:GitHub 上的"分流-修复"自动化层
robomp(代码位于 python/robomp,README 见 python/robomp/README.md)是一个自托管的 GitHub 分流修复机器人。它把 oh-my-pi 的 CLI 编码代理omp以omp --mode rpc模式作为子进程拉起,针对每一个被允许处理的仓库(allowlisted repository)中新打开的 Issue 执行如下闭环:
- 分类Issue(classify);
- 打标签(apply labels);
- 按分类分支处理:
bug/documentation→ 复现(reproduce)→ 修复(fix)→ 提交 PR;question→ 单条评论回答;enhancement/proposal→ 一条有思考的评论;invalid/duplicate→ 一条简短评论。
后续的 Issue 评论和 PR review 评论会恢复同一个 omp 会话(resume the same omp session),让 Agent 保留此前的推理上下文。编排器(orchestrator)即使在中途重启,dispatcher 也会通过每个 Issue 独立的session_dir以omp --continue恢复同一会话——被中断的任务从先前的推理处继续,而不是从头再来。整个编排器是一个运行在 Docker 内的单 FastAPI 进程,事件状态由 SQLite 持久化。
说明:本文中的
omp即 oh-my-pi 本体提供的编码 Agent(其 RPC 服务端由omp-rpcPython 包封装,见 python/robomp/pyproject.toml 的依赖声明omp-rpc>=0.1.0)。
端到端数据流:从 Webhook 到 omp 子进程
AGENTS.md 将整体数据流概括为一条流水线:
Webhook → durable queue → async dispatcher → per-issue git worktree → omp RPC subprocess + host tools逐环节拆解如下(与源码一一对应):
POST /webhook/github— 使用GITHUB_WEBHOOK_SECRET做 HMAC-SHA256 校验(实现于 python/robomp/src/server.py 与 python/robomp/src/github_events.py 的verify_signature)。签名错误返回401——这样 GitHub 会停止重试,而不是持续轰炸 5xx。github_events.route()决定处理方式— 返回triage_issue/handle_comment/handle_pr_conversation/handle_review/handle_release_ci/cleanup_workspace/skip之一。Bot 自己产生的事件(*[bot]后缀、user.type == "Bot"、或配置的bot_login)会被丢弃;workflow_run的发布判定(release verdicts)在 Bot 账号守卫之前就被路由——因为修复推送使用的是 Bot 账号(详见 python/robomp/src/github_events.py)。db.record_event()持久化— 以X-GitHub-Delivery作为去重键执行INSERT OR IGNORE,重复投递的事件被静默忽略,端点返回202。queue.WorkerPool._dispatch_loop消费队列— 在BEGIN IMMEDIATE事务下原子认领state='queued'的行,并用进程内_inflight集合(以issue_key为键)做去重。Issue 工作按 Issue 串行化,发布工作按仓库串行化(键为<repo>#release)。并发上限为ROBOMP_MAX_CONCURRENCY(默认 8)。实现见 python/robomp/src/queue.py。sandbox.SandboxManager.ensure_workspace()产出工作区— Issue 工作区位于/data/workspaces/<owner>__<repo>__<n>/repo,分支为确定性的farm/<8hex>/<slug>格式(make_branch实现于 python/robomp/src/sandbox.py,分支名由 Issue 标题哈希 + 净化后的 slug 组成)。ensure_release_workspace()则复用<owner>__<repo>__release/repo,在默认分支上使用按 tag 区分的会话。tasks.*dispatcher 组装TaskInputs并调用worker.run_task()— 以cwd=worktree、持久的session_dir、随机挑选的任务适配模型池拉起omp --mode rpc。若<session_dir>/*.jsonl已存在,则以--continue恢复(见 python/robomp/src/worker.py 的_has_prior_session与_run_rpc_blocking)。子进程内双工具面— Agent 使用 omp内置工具(read / edit / write / bash / lsp,全部限定在工作区内)与
host_tools.py中的主机工具(python/robomp/src/host_tools.py,这是唯一被允许改动 GitHub 或写入审计记录的表层)。终态落库— 成功 → 事件
state='done';异常 →state='failed'并在events.last_error记录脱敏后的 traceback。无论成败,_inflight槽位都会被释放。
从源码看事件路由细节
route()是整条管线的"大脑",它最值得注意的决策逻辑包括:
- 仓库白名单:仓库不在
ROBOMP_REPO_ALLOWLIST中直接skip; workflow_run事件:仅在action == "completed"、release sentinel 已启用、运行分支是默认分支或v<数字>开头的 tag 分支、且 head commit 消息以ROBOMP_RELEASE_COMMIT_PREFIX(默认chore: bump version to)开头时才排队为handle_release_ci;- Issue 事件:
opened/reopened触发triage_issue;closed触发cleanup_workspace(回收工作区,不消耗用户配额); - 评论事件:普通 Issue 评论 →
handle_comment;Bot 自己的 PR 上的会话评论 →handle_pr_conversation(外来 PR 的会话评论目前被有意忽略);PR review comment →handle_review; - PR 事件:
opened/reopened/ready_for_review触发 PR review(受ROBOMP_PR_REVIEW_ENABLED开关控制);closed触发清理; - Directive(指令)机制:维护者
@bot提及(extract_mention用词边界感知的正则剥离提及)、以及ROBOMP_REVIEWER_BOTS中配置的 review bot(如 chatgpt-codex-connector)的评论,会被解析为权威指令directive,连同parse_pragmas解析出的 pragma(如model=/thinking=覆盖项)一起传给 Agent;只有仓库 OWNER 或ROBOMP_MAINTAINER_LOGINS中的账号才有directive_authorizes_impl权限(见is_implementation_authorizer)。
会话持久化与断点续跑:--continue的完整链路
robomp 最关键的可靠性设计是"一次中断、无缝续跑":
- 每次任务在
session_dir(位于工作区下.omp-session/)写入 omp 的 JSONL 会话转录; - 下次任何事件到达同一 Issue 时,
worker._has_prior_session()检查到*.jsonl即给RpcClient追加--continue参数(python/robomp/src/worker.py); - 编排器重启时,
db.reset_stuck_running()把遗留在running状态的行翻回queued,WorkerPool.start()重新入队(python/robomp/src/queue.py),被中断的 Agent 从转录恢复; - 优雅关闭(Phase B)遵循
ROBOMP_SHUTDOWN_DRAIN_TIMEOUT_SECONDS(25s)排水 +ROBOMP_SHUTDOWN_KILL_TIMEOUT_SECONDS(5s)强杀的分段策略,compose 中stop_grace_period: 30s恰好覆盖两者;被关闭中断的行故意保留running,以便下次启动时自动恢复,而不是误标failed。
任务还有"完成提醒"机制:当triage_issue一轮结束后,若bug/documentation分类尚未触及终态工具(gh_open_pr/mark_unable_to_reproduce/abort_task,见_TERMINAL_TRIAGE_TOOLS),驱动会向同一会话注入最多ROBOMP_TASK_COMPLETION_MAX_REMINDERS(默认 2)次"你还没开 PR,请继续"的提示;工作区残留未提交改动时也会注入dirty_state_reminder(python/robomp/src/worker.py)。
工作区沙箱与权限模型
SandboxManager(python/robomp/src/sandbox.py)负责克隆池(clone pool)与每个 Issue 的 worktree 生命周期:
- 共享克隆池位于
/data/workspaces/_pool/<owner>__<repo>/,远程相关操作(clone / fetch / push)全部走可插拔的GitTransport抽象——进程内LocalGitTransport或经 HMAC RPC 转发的ProxyGitTransport(见robomp.proxy_client); - 每个 Issue 一个 worktree,分支
farm/<8hex>/<slug>;后续事件在同一分支上追加修改(一个 Issue 至多一个 PR); - 四类磁盘所有权区:工作区树(单属主,slot UID/GID)、克隆池(多 slot 共享,
root:ompsetgid02770)、语言工具缓存/data/cache/{cargo,cargo-target,rustup,bun-cache}(多 slot 共享)、Agent HOME 模板/srv/agent-home(只读root:root); - slot 隔离:root 进程内按需创建
omp-N槽位用户(UID 2001 起),Agent 子进程以user=slot_uid, group=slot_uid, extra_groups=[omp]运行,slot 回收时扫描/proc清理遗留进程(_reap_slot),防止上一个任务的残留进程观察到下一个任务; - 凭证脱敏:所有 git 子进程输出、日志、审计行经
redact_credentials()将https://user:pw@host改写为https://***@host(python/robomp/src/git_ops.py 的GitCommandError);worker._SCRUBBED_ENV_KEYS进一步保证GITHUB_TOKEN、GITHUB_WEBHOOK_SECRET、ROBOMP_REPLAY_TOKEN、ROBOMP_GH_PROXY_HMAC_KEY绝不进入 Agent 子进程环境(python/robomp/src/worker.py); - 工作区缓存回收:每次任务后剥离
node_modules与工作区私有 bun 缓存(ROBOMP_RECLAIM_WORKSPACE_CACHES=true),把磁盘占用上界从"打开的 Issue 数"降为"并发数"。
双容器信任边界:robomp 编排器与 gh-proxy
docker-compose.yml(python/robomp/docker-compose.yml)定义了"两个容器、一个信任边界"的部署形态:
- robomp— FastAPI + SQLite 事件队列 +
WorkerPool,在/data/workspaces/的 per-issue worktree 中运行omp。持有 HMAC 密钥,从不持有 PAT; - gh-proxy— 位于
internal: true的内部网络上的兄弟容器,持有GITHUB_TOKEN,校验 robomp 发来的 HMAC 签名请求,执行 REST 与git push,仅对api.github.com出站。
关键安全设计:
- compose 服务故意不用
env_file:,而是用每服务的显式environment:白名单,确保.env中的GITHUB_TOKEN只进入 gh-proxy 容器; - 编排器到 gh-proxy 的每次请求带 HMAC-SHA256 签名,±30s 时钟偏差窗口 + 常量时间比较(
hmac.compare_digest); - gh-proxy不映射任何宿主机端口,外部网络
default只用于访问api.github.com; git push在 gh-proxy 内通过git -c http.extraheader=…走临时进程环境变量传 token,.git/config中的 remote URL 永不含凭证;- GitHub 认证模式互斥:
_validate_proxy_or_pat(python/robomp/src/config.py)拒绝同时设置GITHUB_TOKEN与ROBOMP_GH_PROXY_URL + ROBOMP_GH_PROXY_HMAC_KEY,也拒绝"只设 URL 不设密钥"或"两者皆空"。
配置参考:全部ROBOMP_*环境变量
配置由 python/robomp/src/config.py 的 pydantic-settingsSettings模型加载(ROBOMP_*前缀),权威清单在 python/robomp/.env.example。下表覆盖核心项及其默认值:
| 变量 | 默认值 | 作用 |
|---|---|---|
GITHUB_WEBHOOK_SECRET | 必填 | Webhook HMAC-SHA256 校验密钥,须与 GitHub 仓库 Webhook 配置一致 |
ROBOMP_BOT_LOGIN | 必填 | Bot 账号的小写裸提及句柄(生产为roboomp,不带@/[bot],启动时归一化大小写、空白与常见变体) |
ROBOMP_GIT_AUTHOR_NAME/ROBOMP_GIT_AUTHOR_EMAIL | 必填 | Bot 提交的作者身份;gh_push_branch拒绝推送任何不含该身份的分支提交 |
ROBOMP_REPO_ALLOWLIST | 必填 | 逗号分隔的owner/repo白名单 |
ROBOMP_MAINTAINER_LOGINS | 空 | 可选:除 OWNER 外可授权实现工作的维护者登录名(逗号分隔、大小写不敏感、@可选) |
ROBOMP_REVIEWER_BOTS | 空 | 可选:其评论被当作权威指令的 Bot 登录名(无需@bot提及) |
ROBOMP_GH_PROXY_URL/ROBOMP_GH_PROXY_HMAC_KEY | 必填(gh-proxy 模式) | 编排器访问 gh-proxy 的地址与共享 HMAC 密钥;openssl rand -hex 32生成 |
GITHUB_TOKEN | 必填(PAT 模式) | 单进程 PAT 模式;与 gh-proxy 模式互斥 |
ROBOMP_MODEL | anthropic/claude-sonnet-4-6 | 单个模型 id 或逗号分隔的模型池,每个任务均匀随机挑选(pick_model()),id 须与挂载进容器的models.yml(即宿主~/.omp/agent/models.container.yml)匹配 |
ROBOMP_PROVIDER | 空 | 可选 provider 覆盖(传给omp --provider) |
ROBOMP_THINKING | high | 思考级别:off/low/medium/high(源码另支持xhigh/max) |
ROBOMP_MAX_CONCURRENCY | 8 | 并发认领上限 |
ROBOMP_TASK_TIMEOUT_SECONDS | 2400 | 单轮任务超时;另有ROBOMP_TASK_TIMEOUT_HARD_GRACE_SECONDS=60硬超时宽限 |
ROBOMP_REQUEST_TIMEOUT_SECONDS | 120 | omp RPC 单次请求超时 |
ROBOMP_EVENT_MAX_RETRIES | 3 | 瞬时失败事件自动重试次数,退避计划ROBOMP_EVENT_RETRY_DELAYS_SECONDS=30,120,600(带 ±20% 抖动,防全群同步重放) |
ROBOMP_RATE_LIMIT_WINDOW_SECONDS/_DEFAULT/_CONTRIBUTOR/_UNLIMITED | 3600/3/10/ 空 | 按提交者 GitHubauthor_association分层限流;OWNER/MEMBER/COLLABORATOR 与 unlimited 名单自动豁免 |
ROBOMP_QUESTION_AUTOCLOSE_ENABLED/_HOURS/_SCAN_SECONDS | true/4/60 | question 回答后附加"👎 保持开启"提示,作者未在时限内 👎 则自动关闭为state_reason=completed;后续评论或外部关闭会同步取消排期 |
ROBOMP_RELEASE_SENTINEL_ENABLED | false | 默认关闭的发布哨兵(release sentinel);开启前需订阅Workflow runs事件并给 PAT 加Actions: Read |
ROBOMP_RELEASE_COMMIT_PREFIX | chore: bump version to | 识别发布提交的前缀 |
ROBOMP_RELEASE_MAX_ROUNDS | 5 | 修复轮次上限;ROBOMP_RELEASE_TASK_TIMEOUT_SECONDS=3600控制每轮;可选ROBOMP_RELEASE_MODEL独立选择发布模型池,否则回落到ROBOMP_MODEL |
ROBOMP_PR_REVIEW_ENABLED | true | 是否启用 PR review |
ROBOMP_ISSUE_INDEX_SYNC_SECONDS | 900 | 本地 Issue/PR 搜索索引(SQLite FTS 镜像)的 GitHub 周期调和间隔;<=0关闭调和器 |
ROBOMP_NATIVES_CACHE_ENABLED等 | true | pi-natives 构建产物硬链接缓存(按 git tree-hash 键控),避免每个工作区重复编译 |
ROBOMP_RECLAIM_WORKSPACE_CACHES | true | 每任务后回收依赖缓存 |
ROBOMP_SHUTDOWN_DRAIN_TIMEOUT_SECONDS/_KILL_TIMEOUT_SECONDS | 25/5 | 优雅关闭排水/强杀窗口,两者之和必须低于 composestop_grace_period |
ROBOMP_OMP_COMMAND | omp | 容器内 omp 可执行文件(镜像自带/usr/local/bin/ompshim) |
ROBOMP_WORKSPACE_ROOT/_SQLITE_PATH/_LOG_DIR | /data/workspaces//data/robomp.sqlite//data/logs | 容器内固定路径 |
ROBOMP_BIND_HOST/ROBOMP_BIND_PORT | 0.0.0.0/8080 | Webhook 接收地址 |
ROBOMP_REPLAY_TOKEN | 空 | 可选:启用POST /replay的令牌,留空禁用 |
调试提示:
.env同时被两种方式读取——docker compose做${VAR}插值(配合每服务显式 allowlist),以及本地 CLI 的Settings加载器;务必二选一填写模式块。
开发命令:从源码运行到 Docker 内环
任务运行器统一为 monorepo 根 package.json 的bun配方(robomp:*命名空间),robomp 自身不再携带package.json:
# 本地 venv(无需 docker) bun run robomp:install # pip install -e 'python/robomp[dev]' # 测试 bun run test:py # pytest -x python/omp-rpc/tests python/robomp/tests bun run robomp:test:integration # ROBOMP_INTEGRATION=1,要求 omp 在 PATH 上 # 宿主机直跑编排器 bun run robomp:serve # python -m robomp serve # Docker 内环 bun run pi:image # 构建 oh-my-pi/pi:dev(pi 变更时重建) bun run pi:run # docker run -it oh-my-pi/pi:dev(冒烟测试 shim) bun run robomp:build # pi:image(如 pi 变更)+ docker compose build bun run robomp:dev # build + up -d + 跟随日志 bun run robomp:up / robomp:down / robomp:restart / robomp:logs bun run robomp:rebuild # docker compose build --no-cache bun run robomp:reset # down -v 并删除 pi 镜像 # 前端(Vite + SolidJS,位于 python/robomp/web) bun run robomp:web:dev # vite dev server,代理到 :8080 bun run robomp:web:build # 产出 src/static/ 静态包 bun --cwd=python/robomp/web run check:types # tsc --noEmit容器内 CLI(console scriptrobomp→ python/robomp/src/cli.py 的robomp.cli:main)没有根别名,直接调用:
docker compose --project-directory python/robomp exec robomp robomp triage owner/repo#N docker compose --project-directory python/robomp exec robomp robomp replay <delivery_id> docker compose --project-directory python/robomp exec robomp robomp status docker compose --project-directory python/robomp exec robomp robomp cleanup owner/repo#NHTTP / sqlite / webhook 检查同样无别名:
curl http://localhost:${ROBOMP_BIND_PORT:-8080}/{healthz,readyz,events,issues} docker compose --project-directory python/robomp exec robomp sqlite3 /data/robomp.sqlite首次部署流程(来自 python/robomp/README.md):
cp python/robomp/.env.example .env openssl rand -hex 32 # ROBOMP_GH_PROXY_HMAC_KEY openssl rand -hex 32 # GITHUB_WEBHOOK_SECRET bun run pi:image # 一次性构建 oh-my-pi/pi:dev bun run robomp:build && bun run robomp:up curl -fsS http://localhost:8080/healthz镜像分层设计使构建失效范围可控:只改 robomp 的 Python 只触及运行时层;改 pi 源码才重建oh-my-pi/pi:dev(robomp 的 Dockerfile.robomp 通过FROM ${PI_BASE}继承,默认oh-my-pi/pi:dev,toolchain 全部来自 pi-base,本文件不重复)。PI_ROOT默认指向上级 oh-my-pi 检出(构建上下文与/work/pi只读挂载),仅在不同 checkout 时覆盖;容器内路径恒为/work/pi。
代码规范与工程约定
AGENTS.md 的 "Code Conventions" 章节定义了仓库级的硬性约定(均可对照源码验证):
- Python ≥3.11(容器为 3.12-slim),公共函数强制类型注解,默认
from __future__ import annotations; - 不可变值类型用
@dataclass(slots=True, frozen=True),如github_client.IssueInfo、sandbox.Workspace、db.EventRow; - 异步风格:FastAPI 处理器与
queue.WorkerPool是 async;而worker.run_task必须保持同步并在工作线程中执行(omp-rpc是阻塞的),CLI 命令用asyncio.run包装; - 配置:pydantic-settings
Settings(python/robomp/src/config.py),统一经@cache单例get_settings()访问;测试改动 env 后必须调用reset_settings_cache(); - 依赖注入:
Settings、Database、GitHubClient、SandboxManager显式注入create_app()/WorkerPool/ToolBindings,除get_settings/get_database两个单例访问器外没有模块级全局变量; - 状态唯一真源是 SQLite(
db.Database):events、issues、releases、tool_calls四张表;release 行流转awaiting_ci → fixing → green → failed → superseded;内部_lock保证线程安全,BEGIN IMMEDIATE处理认领竞争;内存态只有WorkerPool的_inflight; - 错误处理:自定义异常族(
GitHubError带retry_after、GitCommandError、InvalidIssueRef、RpcCommandError),错误字符串严禁包含带凭证的 URL; - 日志:结构化 JSON(
logging_config.JsonFormatter),logger.info("event", extra={...}),注意不要与_RESERVED键冲突,configure_logging()只配置一次; - 主机工具范式:每个工具由按任务构建的
ToolBindings闭包组成,经_audit()写入tool_calls审计;审计只见 Agent 提供的参数,永不见内部凭证;新工具遵循"校验参数 → 调GitHubClient/SandboxManager→ 返回结构化 dict → 审计"的固定流程; - 命名:Python 全 snake_case,模块名单数名词,测试文件
test_<module>.py,测试函数test_<action>_<condition>; - 提示词:编辑
src/prompts/*.md,变量用{{path.to.field}},解析在persona._lookup;提示词随包作为数据文件安装(pyproject.toml的package-data),新增提示词无需其他注册。
关键文件速查
| 文件 | 职责 |
|---|---|
| python/robomp/src/server.py | FastAPI 应用:/webhook/github、/healthz、/readyz、/events、/issues、/releases、手动 triage/replay 端点、/仪表盘 |
| python/robomp/src/queue.py | WorkerPooldispatcher 与_inflight串行化、重试、优雅关闭 |
| python/robomp/src/tasks.py | Issue/PR 处理器 +handle_release_ci+cleanup_workspace |
| python/robomp/src/worker.py | 同步 omp RPC 驱动、提示词组装(经persona)、模型挑选、env 清洗 |
| python/robomp/src/host_tools.py | Issue/PR 工具 +release_ci_status/release_job_log/release_retag/abort_task |
| python/robomp/src/sandbox.py | 克隆池 + Issue/release 工作区生命周期、slot 权限、凭证脱敏 |
| python/robomp/src/github_client.py | 类型化 httpx 客户端(含 Actions、tag、GitHub Release 读取) |
| python/robomp/src/github_events.py | 路由与 HMAC 校验 |
| python/robomp/src/db.py | SQLite schema 与 events/issues/releases/tool_calls 的 DAO |
| python/robomp/src/config.py | Settings模型与get_settings() |
| python/robomp/src/cli.py | Click CLI:serve/triage/replay/status/cleanup |
| python/robomp/src/dashboard.py | /单页 HTML 仪表盘 |
| python/robomp/pyproject.toml | 打包 + pytest 配置(asyncio_mode = "auto"、testpaths)、Ruff 规则 |
发布哨兵(Release Sentinel):默认关闭的 CI 修复器
当ROBOMP_RELEASE_SENTINEL_ENABLED=true时,完成的workflow_run事件驱动一个默认关闭的发布哨兵(python/robomp/src/tasks.py 的handle_release_ci):
- 在可复用的
main工作区中诊断失败的发布 CI; - 原子推送修复提交与既有发布 tag;
- 在下一个 verdict 上恢复同一会话,直到每次运行与 GitHub Release 都变绿。
细节规则:只有 head commit 主题以ROBOMP_RELEASE_COMMIT_PREFIX开头、且运行分支为默认分支或v<版本>tag 的 completed 运行才参与匹配;远端 tag 已不再指向该运行 SHA 的过期事件被忽略;cancelled/skipped/neutral/ 过期运行不阻塞收尾;只有在所有运行均无阻塞性结论且存在非 draft 的 GitHub Release 时才进入green。终态有意不在 GitHub 上发言——通过仪表盘 Releases 表、GET /releases?limit=N或robomp status检查。release_retag是唯一的终端工具,负责原子推进main与 tag。
测试与质量保障
- 框架:pytest,
asyncio_mode = "auto"(python/robomp/pyproject.toml);HTTP 模拟统一用httpx.MockTransport(respx虽在 dev 依赖中但库内不使用,新测试应保持同一风格); - Fixtures(python/robomp/tests/conftest.py):
env通过monkeypatch设置全部必需ROBOMP_*变量并前后调用reset_settings_cache();settings触发ensure_paths();db提供隔离的tmp_path/test.sqliteDatabase; - 隔离规则:任何用
monkeypatch.setenv改 env 的测试必须reset_settings_cache(),以失效@cache的单例get_settings(); - 异步测试:
test_github_client.py与test_host_tools.py用后台线程中的自定义事件循环桥接同步风格测试与异步客户端;新测试优先 pytest-asyncioauto模式(async def test_*); - Mock 原则:绝不 patch 内部实现;HTTP 用
httpx.MockTransport注入测试替身,存储用db/tmp_pathfixtures;sandbox 测试用本地真实 bare 仓库充当 upstream; - 集成测试:python/robomp/tests/test_worker_smoke.py 由
ROBOMP_INTEGRATION=1门控(pytestmark.skipif),需要omp在PATH上,默认的bun run test:py不会启用它;它会在httpx.MockTransport的 GitHub 与本地 bare 仓库之上拉起真实omp --mode rpc; - 覆盖率预期:当前约 80 个单元测试;含控制流分支的新代码需要测试覆盖;新 host 工具至少要有一条 happy path + 一条校验失败路径(对照
test_host_tools.py);断言可观察效果(DB / HTTP 请求),而非字面字符串或默认配置值。
安全态势小结
GITHUB_TOKEN只存在于 gh-proxy 容器;编排器若在自己的环境中看到GITHUB_TOKEN会拒绝启动(见 python/robomp/src/cli.py 的_require_proxy_mode);- 编排器 → gh-proxy 为 HMAC-SHA256 签名(±30s 偏差窗口、常量时间比较);
- 坏的 webhook 签名返回
401(让 GitHub 停止重试),绝不返回 5xx; - 推送前门禁(
gh_push_branch):分支必须匹配工作区分支、工作树必须干净、origin/<default>..HEAD上每个提交都必须携带ROBOMP_GIT_AUTHOR_NAME+ROBOMP_GIT_AUTHOR_EMAIL;Agent 在git commit -m 'a\n\nb'中写死的 shell 字面\n转义会被改写为真实换行(仅消息,树/身份/日期保留); - 开 PR 前门禁(
gh_open_pr):仓库定义了脚本时先跑bun run fix(任何 diff 并入 Agent 的 HEAD 提交,不产生独立style:噪音提交),再跑bun check,最后跑完整bun run test(1 小时预算);任一步失败都以RpcCommandError返回给 Agent 迭代,且不创建 PR;skip_checks=true可绕过三者但绕过动作会记录在tool_calls; gh_open_pr还强制校验 PR 正文包含## Repro/## Cause/## Fix/## Verification四个标题以及Fixes/Closes/Resolves #N引用。
常见问题排查
| 症状 | 检查项 |
|---|---|
401 invalid signature | GITHUB_WEBHOOK_SECRET与仓库 Webhook 配置不一致 |
容器报PI_ROOT … missing | 容器内/work/pi挂载为空;宿主机应从python/robomp/运行 docker compose(PI_ROOT默认../..),或导出指向有效 oh-my-pi checkout 的PI_ROOT |
git push: Authentication required | Bot PAT 缺 push 权限,或ROBOMP_BOT_LOGIN未对应 PAT 账号的提及句柄(生产为roboomp,不带@/[bot]) |
refusing to push: commit author identity mismatch | 存在非ROBOMP_GIT_AUTHOR_*身份的提交;错误会列出 SHA,git commit --amend --reset-author --no-edit修复 |
refusing to push: working tree is dirty | 有未提交的 Agent 编辑;或直接调用gh_open_pr(它会自动提交bun run fix的输出) |
bun check failed before PR creation | 修复报错后重试gh_open_pr |
refusing to open PR: \bun run test` failed before open PR| 仓库默认分支套件为红;修复并提交,若失败在改动前已存在可用skip_checks=true` | |
Failed to load pi_natives | 架构不符或缺 native;bun run pi:image后bun run robomp:build |
No API key found for <provider> | ~/.omp/agent/models.container.yml挂载缺失,或 provider id 与ROBOMP_MODEL不匹配 |
结语
robomp 把"事件路由、会话续跑、工作区沙箱、双容器隔离、全量审计"组装成一个可自托管的 GitHub 自动化闭环,而其内部每一步——HMAC 校验、BEGIN IMMEDIATE认领、--continue恢复、slot 权限模型、host-tools 审计——都能在 oh-my-pi 仓库的源码与测试中找到对应实现。若你想让某类 Issue 在自家仓库里自动完成"分类 → 复现 → 修复 → PR",本文给出的架构拆解、配置清单与开发命令已足以支撑你完成部署与二次开发。
【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考