AdamAwan/LubbDubb/tree/main/pr-assistant
pr-assistant
协助处理拉取请求:/pr-assistant:pr map 将其绘制成一页,/pr-assistant:pr walk 绘制同一张图,并在旁边显示停靠点、差异和备注的面板,带你逐项查看。
关于这个 mod
<p align="center"> <picture> <source media="(prefers-color-scheme: dark)" srcset="docs/brand/logo-dark.svg"> <img src="docs/brand/logo.svg" alt="LubbDubb" width="120"> </picture> </p>LubbDubb
为一名软件工程师的工作提供自托管、持续运行的编排框架——一座会观察你的输入(议题、PR、CI、审查评论)的_驾驶舱_,在心跳时刻决定要做什么,并派 Claude Code 代理执行;只有确实需要判断的事项才会升级给你。
名称来自心跳:服务器的核心是驱动一切的周期性脉冲。
在哪里阅读什么。
docs/mission.md说明它为什么存在,以及它改变了这份工作的哪些部分。本文件是总览:框架做什么、工作如何流转,以及第一天就重要的配置。完整工作流(包括另一种工作流如何接入)见docs/workflow.md。docs/spec/是应用每个部分当前行为的规范,以事实写成——每个配置键、每条调度规则、代理运行时、API 与驾驶舱都在其中。如果你想了解下面任何内容的细节,那里都有。docs/feature-timeline.md记录了它如何发展至今。
脉冲
由心跳驱动的重复循环(舰队繁忙时默认 heartbeatIntervalMs 为 30s;空闲时 idleHeartbeatIntervalMs 为 5 分钟),也可以按需触发:
snapshot the world → diff against the last snapshot → reconcile plans
→ decide (dispatcher) → execute (executor) → audit
每一步都会被记录。调度器的理由、它发出的每个动作,以及每个动作的结果都会持久化——因此空闲循环和繁忙循环一样容易解释。代理在池化的 git 工作树(代码工作)或临时目录(桌面工作)中运行,并通过带类型的工具通道回报。
它做什么
按它在循环中的位置分组。每一行都链接到负责它的规范。
接收工作
| 功能 | 说明 |
| --- | --- |
| 主动选择监视 | 一个 ${labelPrefix}-watch 标签决定议题和拉取请求中哪些内容会被处理。标签之外的内容不会被触碰。 → [06][s06] |
| 两个提供方 | GitHub 和 Azure DevOps 通过按能力划分的接口接入,另有一个供整套测试与演示运行使用的 fake 提供方。 → [15][s15] |
| 追踪器状态 | 如果提供方有工作流状态,领取工作会受这些状态限制,框架会把项目移到进行中和审查中。 → [06][s06] |
| 优先级与顺序 | 优先级标签影响领取;排序后的计划以接下来显示,并在当前余量处设定截断线,你可以重新排序。 → [05][s05] |
| 工单看板 | 所有打开和已关闭的项目都可显示为表格,或显示为按状态分列、可拖动卡片的看板——卡片落下前会说明这次投放的成本。 → [17][s17] |
| 附件 | 附在简报上的图片会跟随议题交给实际处理它的代理,并存放在所有工作树之外。 → [12][s12] |
决策
| 功能 | 说明 | | --- | --- | | 规则流水线 | 二十多条命名规则按声明的顺序执行,每条都从世界状态提出工作。顺序是数据,不是评论里的数字。 → [05][s05] | | 受限词汇表 | 调度器只能请求十一种经过验证的动作之一;格式错误的请求会被拒绝并审计,绝不会执行。 → [05][s05] | | 逐项 CI 策略 | 针对_哪一项_检查变红来决定处理方式——修复它、在指导下修复、因为不属于我们而暂缓,或只升级一次。 → [02][s02] | | 决策日志 | 已执行、延期、拒绝和跳过的动作一律记录,并附上理由及产生它的规则;还可以展开查看这条规则为什么存在。 → [18][s18] | | 冷却与上限 | 按来源设置尝试上限与冷却时间,让持续失败的调度转为升级,而不是循环重试。 → [05][s05] |
执行工作
| 功能 | 说明 | | --- | --- | | 工作树中的代理 | 有限的检出池租给各个分支,并通过切换而非重建来复用;重新回来的分支已经预热。 → [09][s09] | | 带类型的工具通道 | 代理回调的 MCP 服务器——读取世界状态、提交它们学到的内容、打开拉取请求、报告检查结果。 → [11][s11] | | 权限后备机制 | 代理遇到允许列表之外的命令时会询问你,而不是卡住。 → [11][s11] | | 每条规则的模型 | 每条调度规则都分配命名配置——包括模型和运行深度——因此冲突修复和计划不会按同一价格计算。 → [02][s02] | | 作业与排程 | 可以从驾驶舱排入临时提示,也可以按 cron 表达式为你排程;两者都像其他工作一样等待空位。 → [13][s13] | | 崩溃恢复 | 重启后失去归属的代理会被停放,脉冲也会暂停,直到你恢复、重新排队或移除每一个代理。 → [10][s10] | | 实时转录 | 点击代理即可阅读它正在做什么、向它输入内容,并查看它在运行中途产出的结果。 → [10][s10]、[12][s12] |
拉取请求
| 功能 | 说明 | | --- | --- | | 健康谓词 | CI 失败、落后于基线或与基线冲突、未处理的审查线程、已准备合并——每个分支一个代理,先处理最重要的问题。 → [07][s07] | | 舰队审查 | 默认关闭:框架会先自行读取拉取请求,再请人介入,并通过分流决定审查深度。 → [07][s07] | | 回复审查 | 每个未处理线程交给一个代理,通过框架回复——在提供方自己的线程中签名、记录并解决。 → [07][s07] | | 堆栈 | 一个部分建立在它依赖的部分之上;基线变红归因于拥有它的 PR,合并按自下而上进行。 → [07][s07] | | 等待你处理 | 某人分配给你的拉取请求、提出请求的人,以及它已经搁置了多久。 → [17][s17] |
按目标分流
| 功能 | 说明 |
| --- | --- |
| 目标评估 | 这里有可以据此工作的目标吗?如果没有,拒绝会在工单上说明缺少什么;目标文字改变后,拒绝会解除。 → [08][s08] |
| 规划 | 一个拉取请求,或拆成依赖链的多个部分,每部分都有自己的分支和范围——也可能判断为「这已经完成」。 → [08][s08] |
| 计划批准 | 始终需要。计划会携带风险、范围外内容,以及任何人如何确认它已经生效;还可以与对话式规划器讨论。 → [08][s08] |
| 评估 | 评估交付的内容,而不是代理的信心。它的 no 分支会重新规划、增加部分,或升级处理。 → [08][s08] |
| 验证 | 只有运行交付物才能回答的检查,写给人执行——或交给你自己的 Claude Code。 → [20][s20] |
| 回顾 | 每个交付目标一份总结,来源是共享暂存区,以及框架自己记录的成本。 → [13][s13] |
| 收尾 | 框架从不关闭工单。它会用你的名字登记一项持续义务;当追踪器不再把工单列为打开时,这项义务才结清。 → [13][s13] |
落地之后
| 功能 | 说明 |
| --- | --- |
| 环境 | 默认关闭。记录每个 PR 落地时的提交,以及每个环境是否已经包含它——通过你自己的命令询问,结果有三种状态。 → [24][s24] |
| 到达 | 到达某处可以触发交付目标应向你履行的事项,也可以在工单上增加一行;每个环境都可选择开启。 → [24][s24] |
| 部署后监视 | 目标声明运行中的系统必须展示什么;到达会打开一个窗口,系统遥测按计划被询问。这里没有模型参与。 → [29][s29] |
| 障碍 | 阻挡舰队前进的事项按键处理而不是按文字匹配:两个独立的声音、一个负责人,以及永远不是人的退出条件。 → [27][s27] |
| 跨舰队池 | 位于 fleet 之上的那一层:共享仓库中每个舰队一个命名空间,配有相互印证模型和摘要。 → [28][s28] |
观察框架自身
| 功能 | 说明 | | --- | --- | | 需要你处理 | 一条轨道集中显示升级、计划批准、对外提案、权限请求、配置健康状况,以及一次交付留下的义务。 → [17][s17] | | 洞察 | 运行花费了什么、产出了什么、最终结果是什么——另外还有实时燃烧监视,会显示成本达到其桶中位数数倍的运行。 → [18][s18] | | 跑道 | 以舰队时间衡量舰队是否还有剩余工作——以及没有剩余工作的原因是否是你。 → [25][s25] | | 配置健康 | 每个可能让舰队悄然停摆的设置占一行,每行最后都要针对真实世界检查,而不是给出建议。 → [26][s26] | | 错误日志 | 所有捕获到的失败都会汇入同一条路径:持久化、镜像到 stderr,并流式传送到驾驶舱。 → [18][s18] | | 自我更新 | 框架观察自己的构建,排空工作,然后交给替换它的监管程序。 → [21][s21] | | 本地运行 | 机器唯一的开发环境,可以从驾驶舱启动和停止,输出显示在舰队已经……
安装
请先查看作者 README 确认 marketplace 和插件名称;命令可能随仓库结构改变。
claude plugin marketplace add AdamAwan/LubbDubb claude plugin install pr-assistant
原文 / README
LubbDubb
A self-hosted, always-running orchestration harness for one software engineer's work — a cockpit that watches your inputs (issues, PRs, CI, review comments), decides what to do on a heartbeat, and dispatches Claude Code agents to do it, escalating to you only what genuinely needs judgment.
The name is the heartbeat: the server's core is a periodic pulse that drives everything.
Where to read what.
docs/mission.mdis why it exists and what it changes about the job. This file is the overview: what the harness does, how work flows through it, and the configuration that matters on day one.docs/workflow.mdis the workflow in full, including where a different one slots in.docs/spec/is the specification of how every part of the application behaves today, written as fact — every config key, every dispatch rule, the agent runtimes, the API, the cockpit. If you want the detail behind anything below, it is in there.docs/feature-timeline.mdis how it got this way.
The pulse
One repeating cycle, driven by a heartbeat (heartbeatIntervalMs, default 30s while the fleet is
busy; idleHeartbeatIntervalMs, 5 minutes, while it is not) and also
triggerable on demand:
snapshot the world → diff against the last snapshot → reconcile plans
→ decide (dispatcher) → execute (executor) → audit
Every step is recorded. The dispatcher's rationale, every action it emitted, and the outcome of each action are persisted — so an idle cycle is as explainable as a busy one. Agents run in pooled git worktrees (code work) or scratch dirs (desk work), and report back over a typed tool channel.
What it does
Grouped by where in the loop it sits. Each line links to the spec that owns it.
Taking work in
| Feature | What it is |
| ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Opt-in watching | One ${labelPrefix}-watch tag decides what is acted on, on issues and pull requests alike. Nothing outside it is touched. → 06 |
| Two providers | GitHub and Azure DevOps behind per-capability seams, plus a fake provider the whole suite and the demo run on. → 15 |
| Tracker states | Where the provider has workflow states, pickup is gated on them and the harness moves the item to in-progress and in-review. → 06 |
| Priority and order | Priority labels weight pickup; the ranked plan ships as Up next with a cut-line at current headroom, re-orderable by you. → 05 |
| Tickets board | Every open and closed item, as a table or a column-per-state board you drag cards across — the drop's cost said before it lands. → 17 |
| Attachments | Images attached to a brief follow the issue to whichever agent works it, stored outside every worktree. → 12 |
Deciding
| Feature | What it is | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | | A rule pipeline | Two dozen named rules walked in a declared order, each proposing work from the world. The order is data, not numbers on a comment. → 05 | | A bounded vocabulary | The dispatcher can only ever ask for one of eleven validated actions; anything malformed is rejected and audited, never executed. → 05 | | Per-check CI policy | What to do about which check went red — fix it, fix it with guidance, hold it because it is not ours, or escalate once. → 02 | | The decision log | Executed, deferred, rejected and skipped alike, each with its reason and the rule that produced it, expandable to why that rule exists. → 18 | | Cooldowns and caps | Per-origin attempt caps and cooldowns, so a dispatch that keeps failing escalates instead of looping. → 05 |
Doing the work
| Feature | What it is | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | Agents in worktrees | A bounded pool of checkouts leased to branches and switched rather than recreated, so a branch that comes back starts warm. → 09 | | A typed tool channel | An MCP server agents call back on — read the world, raise what they learned, open a pull request, report a check. → 11 | | A permission backstop | An agent hitting a command outside the allow-list asks you rather than hanging. → 11 | | Models per rule | Named profiles — a model and the depth it runs at — assigned per dispatch rule, so a conflict fix and a plan are not priced alike. → 02 | | Jobs and schedules | An ad-hoc prompt queued from the cockpit, or queued for you on a cron expression. Both wait for a slot like everything else. → 13 | | Crash recovery | Agents orphaned by a restart are parked, and the pulse is held, until you restore, requeue or remove each one. → 10 | | Live transcripts | Click an agent and read what it is doing, type into it, and see what it produced mid-run. → 10, 12 |
Pull requests
| Feature | What it is | | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | Health predicates | Failing CI, behind or conflicting with its base, unhandled review threads, ready to merge — one agent per branch, top concern first. → 07 | | The fleet review | Off by default: the harness reads a pull request of its own before a person is asked, with a triage that picks how thoroughly. → 07 | | Answering a review | Every unhandled thread goes to one agent, replied to through the harness — signed, recorded, and resolved on the provider's own threads. → 07 | | Stacks | A part is based on the part it depends on; a red base is attributed to the PR that owns it, and merging is bottom-up. → 07 | | Waiting on you | A pull request somebody assigned you, who asked, and how long it has been sitting there. → 17 |
The funnel, per goal
| Feature | What it is |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Goal appraisal | Is there a goal here to work from? A refusal says what is missing, on the ticket, and lifts when the goal text changes. → 08 |
| Planning | One pull request, or a dependency-chained decomposition into parts each with its own branch and scope — or "this is already done". → 08 |
| Plan approval | Always. A plan carries risks, scope-outs and how anyone will know it worked, and can be discussed with a conversational planner. → 08 |
| Assessment | Asked of what was delivered, not of the agent's confidence. Its no arm replans, adds a part, or escalates. → 08 |
| Validation | Checks that can only be answered by running the delivered thing, written for a person — or handed to your own Claude Code. → 20 |
| Retrospective | One write-up per delivered goal, from the shared scratchpad and the harness's own record of what it cost. → 13 |
| Close-out | The harness never closes a ticket. It files a standing obligation with your name on it, which settles once the tracker stops listing it open. → 13 |
After it lands
| Feature | What it is |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Environments | Off by default. The commit each PR landed as, and whether each environment has it yet — asked with your own command, three-valued. → 24 |
| Arrivals | Arriving somewhere can be what opens what a delivered goal owes you, and what puts a line on the ticket. Both opt-in, per environment. → 24 |
| Post-deploy watch | A goal declares what a running system must show; an arrival opens a window, and your telemetry is asked on a schedule. No model in it. → 29 |
| Obstacles | What is in the fleet's way, keyed rather than matched on prose: two independent voices, an owner, and an exit that is never a person. → 27 |
| The cross-fleet pool | The distance above fleet: one namespace per fleet in a shared repository, with a corroboration model and a digest. → 28 |
Watching the harness itself
| Feature | What it is | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | Needs you | One rail: escalations, plan approvals, outbound proposals, permission requests, config health, and the obligations a delivery leaves. → 17 | | Insights | What runs cost, what they yielded and what came out — plus a live burn watch that surfaces a run several times its bucket's median. → 18 | | The runway | Whether there is work left for the fleet, measured in fleet time — and whether the reason there is not is you. → 25 | | Config health | One row per setting that can stop the fleet silently, each ending in a check against the real world rather than advice. → 26 | | The error log | The one path every caught failure funnels through: persisted, mirrored to stderr, streamed to the cockpit. → 18 | | Self-update | The harness watches its own build, drains, and hands off to a supervisor that replaces it. → 21 | | Local runs | The machine's one dev environment, brought up and taken down from the cockpit, with its output in the pane the fleet already has. → 23 | | Pets | A vivarium at the foot of the rail. Your actions drop eggs; the eggs hatch. It gates nothing. → 22 |
The flow of work
Two entry points, one path. A prompt states a goal and a ticket is found or created for it; a ticket states its own. Everything downstream keys on the ticket, so work started from a prompt is as recoverable, reviewable and reportable as work started from the tracker.
flowchart TD
P([Start with a prompt]) --> G[Goal is stated]
T([Start with a ticket]) --> TK
G -- find or create --> TK[Ticket]
TK --> V{Enough information<br/>to proceed?}
V -- no --> AL[Say what is missing,<br/>on the ticket]
AL --> UW([Stop working it])
UW -. the goal text changes .-> TK
V -- yes --> PL[Plan the work]
PL --> AP{Plan accepted?}
AP -- no, revise --> PL
AP -- yes --> WK[/Do the work/]
WK --> QG{Quality gates<br/>review, CI, a person}
QG -- not satisfied --> WK
QG -- satisfied --> M[Merge]
M --> GC{Goal achieved?}
GC -- no --> PL
GC -- yes --> DV[Deliver: validate, write up,<br/>hand back what is yours]
DV --> AR{Configured<br/>environments?}
AR -- yes --> EN[Watch it arrive,<br/>then watch it behave]
AR -- no --> D
EN --> D([Done])
The standard steps
| Step | What happens |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Intake | Every open issue/PR is fetched and shown. What is acted on is decided by the watch tag, plus tracker workflow states where the provider has them. |
| Enough information? | One agent reads the ticket against the repository and says whether there is a goal here to work from. Only an explicit unclear holds anything. |
| Plan the work | A planning agent reads the repo and returns either one PR will do, a decomposition into dependency-chained parts, or this goal is already met. |
| Plan accepted? | Every plan verdict appears in Needs you. Accepting releases the parts to be scheduled; rejecting falls the issue back to the single-PR path, and it can be held. |
| Do the work | An agent per part (or one for the whole issue), each in its own worktree. Code is the most common arm, not the only one — a part may finish with a report. |
| Quality gates | The fleet's own review of the diff (opt-in), then tests, static analysis, pipeline health and human review. Each failing check is classified per check. |
| Merge | A green, approved, mergeable, comment-clear PR is merged — bottom-up for a stack, and never while it is based on another in-flight branch. |
| Goal achieved? | Asked of what was actually delivered, not of the agent's confidence. A no returns to planning, because what is missing may be a different decomposition. |
| Deliver | A validation sheet of checks only a person or a running system can answer; a retrospective written from the record; the tracker state moved and a status comment. |
| Close out | Nothing closes the ticket. A standing obligation with your name on it is filed, and settles itself once the tracker stops listing the item open. |
| Arrival | Where environments are configured: the commit each PR landed as, whether each environment has it yet, and — where declared — whether it is behaving now it is there. |
The gates that carry the loop
Each is a decision something has to make, not a step that always passes.
- Enough information to proceed rejects a goal nothing can act on, before an agent spends itself discovering that. Refusal is not silent — it says what is missing, on the ticket — and it is not permanent: the hold ends when the goal text changes, or when anyone comments.
- Plan accepted is where you see the shape of the work before it happens. There is no switch for it: a plan that started itself can only be undone by a replan, which is strictly worse.
- Quality gates are a set, not a list — tests, static analysis, pipeline health, human review, and the fleet's own review where it is on. The classification is per check, and the third reading is the one that matters: red, but not ours holds and says why, rather than sending an agent at a wall.
- Goal achieved is asked of the delivered work. Its
noarm proposes a replan, a follow-up part, or escalates — depending on what the assessment says fell short.
Priorities when headroom is scarce
The dispatcher ranks every candidate, then applies the concurrency cut. Roughly, highest first:
- An operator queued a job from the cockpit — takes the next free slot.
- A PR with problems: failing CI, a stale or conflicting base, an unhandled review comment.
- A PR that is ready to merge.
- Planning, approval, appraisal and assessment for issues.
- Plan parts, then fresh issue pickup.
PR work runs before new issue pickup, so a PR in trouble is always worked ahead of starting new tickets. The full ordered plan ships to the cockpit as Up next, with a cut-line at the current headroom; you can re-order it, and the override persists.
Where you are in the loop
The harness owns the loop; you own the verdicts. You are asked when — and only when — a decision is genuinely yours:
- Needs you collects escalations, plan approvals, outbound-act proposals, permission requests from agents that hit a command outside the allow-list, the config checks, and what a delivered goal still owes you — a validation sheet to run, a ticket to close.
- Nothing side-effectful leaves without a human, save two standing authorities that are yours: a
stack landing you clicked over named pull requests, and
sendPrRepliesWithoutApproval, on by default, which sends a drafted review reply straight to the thread. A rejection stands until the world gives a reason to ask again — a push, a CI result, an approval, a comment — and the reason you typed is handed to the next agent that works that item. - A restart never decides for you. Agents orphaned by a crash or shutdown are parked, and the heartbeat is held until you restore, requeue or remove each one.
Two things are deliberately fixed: every act reaching the outside world is authorized, and an agent declares that it finished — silence never reads as success.
Getting started
Node >=22.12 and git. A fresh clone installs with npm ci — node-pty is a
native build, so it is not instant (on npm 12+ it builds because package.json
lists it in allowScripts).
npm ci # native dep: node-pty
cp lubbdubb.config.example.json lubbdubb.config.json # your local config (gitignored)
npm start # builds the cockpit, serves on 127.0.0.1:4300
Every key in the config is optional and the harness boots with no file at all — but the defaults
select the real Claude Code runtime, while the shipped example selects the mock one (agentMode: "raw"
against the built-in fake providers). So copy it for a first run and the whole loop turns with no
model or provider credentials. From then on the cockpit's Config page edits that same file
directly, key by key, leaving its comments and ordering alone — hand-editing and the form are two ways
at one file.
npm start builds the cockpit bundle and then runs the server. Two variants matter:
npm run start:server skips the build and serves whatever web/dist already holds, and
npm run serve runs the server under the supervisor that can replace it — which is what
self-update needs, and the way to run it under systemd, NSSM or a long-lived terminal.
→ docs/spec/21
Boot prints what it decided, and the link to open:
[lubbdubb] cockpit listening on 127.0.0.1:4300
[lubbdubb] open the cockpit: http://127.0.0.1:4300/#t=<token>
[lubbdubb] token minted at .lubbdubb/cockpit-token (0600) — reused on the next start
[lubbdubb] heartbeat=300000ms cap=3
[lubbdubb] agent tools: on
Open it once per browser and the cockpit remembers the token. The harness binds loopback only and
every route needs that token, because the cockpit can queue a job — and a job spawns a real agent with
write access to your repo. The token file is gitignored, along with the rest of .lubbdubb/ (the
SQLite database, worktrees, desk scratch dirs and attachments all live under it).
One click: the Claude Code plugin
The harness ships a Claude Code plugin for your own Claude Code: the /lubbdubb:… skills every
Open in Claude Code link in the cockpit calls (/lubbdubb:check 284:C, /lubbdubb:plan 284,
/lubbdubb:ask 284, …), the desktop tool channel those skills talk to, and a notice board: a status
line above your prompt and a panel beside the transcript with what the harness is waiting on you for,
feature progress, pull request states, the fleet and what is up next. Boot writes it to ~/.lubbdubb/plugin:
[lubbdubb] Claude Code plugin 1.0.0-… written to ~/.lubbdubb/plugin — install or update it from the cockpit's MCP tab
Until it is installed the cockpit shows a banner under the top bar; Install it opens the MCP tab,
whose button runs claude plugin marketplace add and claude plugin install for you at user scope, and
removes the hand-registered lubbdubb MCP server and the old /lubbdubb skill if you had them. Restart
open Claude Code sessions to pick it up. Skip it and nothing breaks: every check simply falls to the fleet.
→ docs/spec/11
Then: use Inject event to simulate the world moving (a CI failure, a review comment) and watch the
harness react; click an agent to see its live transcript and type into it; answer items in Needs
you; use New job to launch an ad-hoc prompt, or New schedule to have one queued on a cron
expression (0 9 * * 1-5 — weekdays at nine, read in the harness's own timezone). The Decision log
shows what was decided each cycle and which rule produced it; Activity shows how the world itself
changed.
Needs you also carries the configuration checks — one row per setting that can stop the fleet silently, each ending in a check against the real world rather than a sentence of advice. On a fresh install that rail is the shortest route from the mock loop to a working deployment. → docs/spec/26
Configuration
Every key is optional, and every key, its default and its precedence is in
docs/spec/02-configuration.md. These are the ones that decide
whether a deployment works at all:
| Key | Default | Why it matters |
| ------------------------------ | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| repoRoot | the directory you launch in | The git repository worktrees are cut from. Left alone, the harness works on its own checkout. |
| integrations | all fake | Which provider serves each capability — sourceControl, issues, pool. fake is the mock world the demo and the suite run on. |
| github / azureDevOps | unset | The provider's own target: owner and repo, or organization, project and repository. |
| userId | unset | Who you are to the provider. Pickup reads label authorship, so without it nothing is ever picked up and nothing says why. → 06 |
| ownWorkOnly | true | Whether the world arrives filtered to you: your watch tags, your pull requests. A team decision, so it belongs in the project layer. |
| agentMode | stream | stream runs a real model. raw is the mock agent — argv over a terminal — and what the example config and the tests use. |
| maxConcurrentAgents | 3 | The fleet's one size knob: the worktree pool is this plus a slack of two, read live, so raising it raises the pool with it. |
| defaultBranch | "main" | The integration branch. Not auto-detected, and a PR targeting anything else is treated as stacked. |
| heartbeatIntervalMs | 30000 | The gap between timer-driven cycles while the fleet is busy; idleHeartbeatIntervalMs (5 min) while it is not. Everything is also triggerable on demand. |
| labelPrefix | "lubbdubb" | Derives the one -watch tag. Everything is opt-in; an empty prefix turns the gate off entirely. |
| ci.checks | [] | Per-check policy: dispatch, dispatch with guidance, ignore, or escalate. Empty means every red check gets an agent. → 02 |
| agentModels | unset | Named profiles — a model and the depth it runs at — assigned per dispatch rule. Omitted, no launch carries --model. |
| agentAllowedTools | toolchain + read/skill/web | What an unattended agent may run without asking. Anything else is routed to you rather than hanging. Never set this via claudeArgs. |
| sendPrRepliesWithoutApproval | true | Send a drafted review reply straight to the thread. false is the stricter setting: every draft waits in the inbox. |
| review.enabled | false | The fleet reads a pull request of its own before a person is asked. With two or more review.modes, a triage picks how thoroughly. |
| environments | [] | Where landed work travels: a command per environment printing the commit it is at, optionally what an arrival opens and what to watch for. |
| host / port / auth | 127.0.0.1 / 4300 / on | Loopback and a bearer token. A host reachable off this machine with auth.enabled: false is refused at load. |
A first real deployment is about six lines:
{
"repoRoot": "/path/to/your/repo",
"integrations": { "sourceControl": "github", "issues": "github" },
"github": { "owner": "acme", "repo": "app" },
"userId": "your-github-login",
"agentMode": "stream",
"maxConcurrentAgents": 3,
"defaultBranch": "main"
}
Secrets are never config keys. GITHUB_TOKEN for GitHub, AZURE_DEVOPS_PAT (or a logged-in az
CLI) for Azure DevOps, LUBBDUBB_TOKEN for the cockpit — all from the environment, so
lubbdubb.config.json stays safe to paste. Agents inherit your shell's model credentials; a stray
ANTHROPIC_API_KEY silently moves the whole fleet onto API billing.
Several switches no longer exist. Planning, plan approval, the goal appraisal, the assessment, the retrospective, validation, the tool channel and the permission backstop are all unconditional. A config still naming one of them is warned about and ignored, and the warning says what replaced it. → docs/spec/02
Sharing a config with your team
lubbdubb.config.json is yours and is gitignored. A lubbdubb.project.json committed at the root of
the repository the harness works on is the team's: everyone pointed at that repo picks it up, and
each person's own file wins over it key by key. So the CI routing, the environments, the integration
branch and the tracker's state names are written once in the project, while who you are (userId),
which models you dispatch on (agentModels) and how many agents your machine runs stay local. Every
key is legal in it except repoRoot — the file is found through repoRoot, so it cannot be the
thing that sets it. → docs/spec/02
Development
npm run dev # server with reload
npm run web:dev # cockpit with HMR (proxies /api + /ws)
npm test # unit + integration tests (node:test)
npm run smoke # full end-to-end with real node-pty + a git worktree
npm run check # format, lint, typecheck ×2, knip, test — concurrently, in one shot
npm run check is the gate CI enforces. It runs its stages in parallel and reports every failure
rather than stopping at the first; a warm run costs about as long as the test suite alone.
See docs/spec/19-development.md for the test seams, the coverage and
security workflows, and the hosted GitHub Pages demo build.
Documentation
| Where | What it is |
| ------------------------------------------------------ | ------------------------------------------------------------------------------ |
| docs/operating.md | How to operate the harness: what changes about the job, and what stays yours |
| docs/operating.html | The same guide as a page to skim — open it in a browser |
| docs/workflow.md | The end-to-end workflow, its variation points, and what is narrower than drawn |
| docs/spec/ | The specification of every subsystem as it behaves today |
| docs/feature-timeline.md | What landed when, from the walking skeleton onwards |
| docs/prompt-templates/ | The rule dispatcher's built-in prompt bodies, ready to override |
| CLAUDE.md | Operating notes for agents working in this repo — the sharp edges |
License
MIT. Copyright (c) 2026 Adam Awan.

