运维
正常运行完全自动:timer 触发 tick,tick 最多做一件事,进度自己发布到 journal 和 GitHub。正常路径永远不需要 status 命令、轮询或督工——下面的 命令只用于首次验证、排查和恢复。Timer
systemd/muyan-pilot.timer 每刻钟触发一次,全天 24 小时
(OnCalendar=*-*-* *:00/15、AccuracySec=30s、Persistent=false——
错过的 tick 被丢弃,从不排队)。每个 tick 启动
muyan-pilot.service,它:
- 先 fast-forward 代码(
ExecStartPre,在 Python 进程外):git fetch origin main && git merge --ff-only origin/main。checkout 不干净、fetch 失败或无法 fast-forward 时 preflight 失败:service 不 启动,原因写入 systemd journal(fail fast)。正在运行的长任务从不被 热更新或杀掉——service active 时 systemd 忽略 timer 的 start 请求, 下一次真正启动取到最新代码。 - 运行一个 tick:恢复一个已打开的 PR(review/fix/merge),或领取
一个
ai-readyIssue,然后退出。在任何 slot 或领取之前,tick 还 运行启动前 git transport 检查(Issue #114):部署 checkout 的 配置的originremote 必须是第一个配置 source repo 的 SSH 形式,且git ls-remote <ssh-url>退出 0(SSH 可达且已认证)。 传输损坏记录结构化transport_check_failed ... reason=...行并让 启动失败——不取 slot、不领取、不改标签,没有 HTTPS 回退(git 数据操作,包括.github/workflows/*.yml推送,永远走 SSH;GitHub API 操作留在ghtoken 上)。
日志(journal)
journal 是本地记录。一个 run 的每行都以 run id 前缀[<run_id>] 开头,
一条 grep 还原完整时间线:
key=value 行:
run_start/run_end— 开始时的完整现场(branch、worktree、 session 文件),结束时的结果(PR URL、commit);activity/heartbeat/model_wait/resumed— session 运行 期间的实时 Pi 活动(phase、最近动作、elapsed、idle);pi_idle— 超过 300 秒(PI_IDLE_WARN_SECONDS=300)没有 model/session 活动且模型不期望回复时的一次 WARNING;活跃的慢模型 (model_wait)从不告警;run_failed— 完整现场加原因(pi_exit_N、timeout_...s,或upstream_dead_stale_...s——冻结的model_wait超过PI_MODEL_WAIT_DEAD_SECONDS(默认 600 秒)判定上游模型已死,Runner 杀掉 Pi)。
CLI(muyan_pilot.py)
--config 或 MUYAN_PILOT_CONFIG 环境变量接收配置
(默认 muyan-pilot.toml)。status 和 session 是调试附件——
journal 和 GitHub 仍是正常可观测路径。
Worktree 与 base 新鲜度
每次领取先 fetch 并冻结origin/<base_branch>,任务 worktree 和
feature branch 都从那个精确 SHA 创建——绝不来自主工作区当前 HEAD。
branch 和 worktree 名带 run id(例如
.worktrees/<...>-issue-14-e07383c2),所以重试的 Issue 得到新的独立
run,旧现场保留。.worktrees/ 已 gitignore。
任务 worktree 共享部署 checkout 的单一 origin remote(git worktree add 创建的 worktree 继承主仓库的 remote 配置),所以 git transport 只
在 checkout 上配置一次,所有 worktree 继承:新 bootstrap worktree
天然有 SSH git remote -v,它们的 fetch/push——包括
.github/workflows/*.yml——走 SSH(Issue #114)。
创建 PR 前,实现者重新 fetch base:如果 origin/<base_branch> 前进了,
它把最新 base 合入 task branch、手工解决冲突、重跑完整测试,然后才
推送。Runner 用 git merge-base --is-ancestor origin/<base_branch> HEAD 验证,拒绝 head 不包含最新远端 base 的交付。
故障恢复
run 产物(plan、test log、session JSONL)留在任务 worktree 作为本地
记录;GitHub 承载交付记录。
并发
max_concurrency(默认 1)限定本机并发交付数。slot 是
<repo_dir>/.muyan-pilot/slots/slot-N 上的排他 flock(2) 锁,在任何
领取之前取得,整个交付生命周期(implement → review → merge)持有;
进程无论如何退出,内核都会释放它。拿不到 slot 的 Runner 记录
capacity_full 后退出,不领取 Issue。