Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

Docs: best of N in the API reference, audit log and limits (#112)

#108 documented attempts on the Tasks page. The API reference had no
entries for comparing or picking attempts, nor attempts on starting a
task; the audit log lacked task.pick; and Limits had no attempts row.
The project reference also now lists plan_first among its settings.


Claude-Session: https://claude.ai/code/session_016q5wwBFck8JHq14gHUM2CL

Co-authored-by: Claude <noreply@anthropic.com>
Canberk Demirci authored · GitHub committed signed1 parent c859db1commit e438b3bd3cd42e930613c2d59dc92f01e7fa9415Browse files
3 files changed+18−3
modifiedapps/diffinery/docs/reference/limits.md+1−0View file
@@ -236,6 +236,7 @@
236236 | Installing the runtime | 10 minutes | Installing Claude Code or Codex in the sandbox. |
237237 | Goal and follow-up length | 20,000 characters | ``422 `goal` is longer than 20000 characters`` (or `body` for a follow-up). |
238238 | Plan length | 20,000 characters | A [plan-first](/docs/agents/tasks#plan-first) run's plan is cut there and ends with `… (plan cut at 20000 characters)`. |
239+| Attempts per task | 1 to 4 | ``422 `attempts` must be a whole number from 1 to 4``. Each attempt counts toward how many tasks run at once. A task on a pull request's branch runs one. See [Best of N](/docs/agents/tasks#best-of-n). |
239240 | Custom command length | 2,000 characters | ``422 `command` is longer than 2000 characters``. |
240241 | Transcript | 20,000 lines per run, 32 KiB per line | Later output lines of a run aren't stored (Diffinery's own notes still are); a longer line is cut. |
241242 | Transcript lines per MCP call | 500 | `agent_task` returns at most 500 lines after `after`. |
modifiedapps/diffinery/docs/security/audit-log.md+2−1View file
@@ -74,9 +74,10 @@
7474
7575 | Action | Recorded when | Target | Detail |
7676 |---|---|---|---|
77-| `task.start` | Someone starts an [agent task](/docs/agents/tasks) | `owner/repo#<task number>` | `agent`, `runtime`; `provider` and `model` when it goes through OpenRouter; `plan_first` when it plans first |
77+| `task.start` | Someone starts an [agent task](/docs/agents/tasks) | `owner/repo#<task number>` | `agent`, `runtime`; `provider` and `model` when it goes through OpenRouter; `plan_first` when it plans first; `attempts` when it runs more than one |
7878 | `task.message`, `task.retry` | The person who started a task sends a follow-up or retries it | `owner/repo#<task number>` | none |
7979 | `task.plan_approve` | The person who started a [plan-first](/docs/agents/tasks#plan-first) task approves its plan | `owner/repo#<task number>` | none |
80+| `task.pick` | The person who started a task with [attempts](/docs/agents/tasks#best-of-n) picks one | `owner/repo#<task number>` | `attempt` |
8081 | `task.plan_split` | The person who started a plan-first task hands its plan to their coordinator to split | `owner/repo#<task number>` | `post`, `coordinator_task` |
8182 | `task.stop` | Someone stops a task (its starter, its agent or a repository admin) | `owner/repo#<task number>` | none |
8283 | `project.memory` | Someone saves or restores the [project](/docs/agents/projects) memory | `owner/repo` | `version`, `length`, and `restored` (the version restored) for a restore |
modifiedapps/diffinery/src/docs/api-meta.ts+15−2View file
@@ -63,6 +63,7 @@
6363 "POST /repos/{owner}/{repo}/diffinery/tasks/{number}/messages": { scope: "write", sessionOnly: true },
6464 "POST /repos/{owner}/{repo}/diffinery/tasks/{number}/retry": { scope: "write", sessionOnly: true },
6565 "POST /repos/{owner}/{repo}/diffinery/tasks/{number}/plan/approve": { scope: "write", sessionOnly: true },
66+ "POST /repos/{owner}/{repo}/diffinery/tasks/{number}/attempts/{attempt}/pick": { scope: "write", sessionOnly: true },
6667 "POST /repos/{owner}/{repo}/diffinery/tasks/{number}/plan/split": { scope: "write", sessionOnly: true },
6768 "POST /repos/{owner}/{repo}/diffinery/project/posts": { scope: "write", sessionOnly: true },
6869 "PUT /repos/{owner}/{repo}/diffinery/project/memory": { scope: "write", sessionOnly: true },
@@ -1446,12 +1447,13 @@
14461447 agent: ["string", "The agent's login (required)."],
14471448 base: ["string", "Branch to start from (default: the default branch)."],
14481449 plan_first: ["boolean", "`true` to plan in a read-only sandbox before building, `false` to build at once. Left out, the project's default; a project that requires plans ignores `false`. See /docs/agents/tasks#plan-first."],
1450+ attempts: ["integer", "1 (default) to 4: run the first build as that many attempts side by side, then pick one (422 above 1 for a task on a pull request's branch). See /docs/agents/tasks#best-of-n."],
14491451 command: ["string", "For `custom`: the shell command to run."],
14501452 },
14511453 },
14521454 "GET /repos/{owner}/{repo}/diffinery/project": {
14531455 summary: "Get the project",
1454- description: "The repository's project: `settings` (`max_running`, `instructions`), `memory` (`body`, `version`, `updated_by`, `updated_at`) and `board`, the agent tasks by column (`waiting`, `working`, `queued`, `review`, `landing`, `done`, `idle`). Agent reviews aren't on the board. Needs write access. See /docs/agents/projects.",
1456+ description: "The repository's project: `settings` (`max_running`, `instructions`, `plan_first`), `memory` (`body`, `version`, `updated_by`, `updated_at`) and `board`, the agent tasks by column (`waiting`, `working`, `queued`, `review`, `landing`, `done`, `idle`). Agent reviews aren't on the board. Needs write access. See /docs/agents/projects.",
14551457 access: "write",
14561458 },
14571459 "GET /repos/{owner}/{repo}/diffinery/project/posts": {
@@ -1516,9 +1518,20 @@
15161518 },
15171519 "GET /repos/{owner}/{repo}/diffinery/tasks/{number}": {
15181520 summary: "Get an agent task",
1519- description: "The task, its runs and its pull request. Needs write access. A plan-first task also has `plan_state` (`planning`, `waiting`, `approved` or `split`) and `plan`; a task split from a plan has `parent_task`.",
1521+ description: "The task, its runs and its pull request. Needs write access. A plan-first task also has `plan_state` (`planning`, `waiting`, `approved` or `split`) and `plan`; a task split from a plan has `parent_task`. A task with more than one attempt has `pick_state` (`running`, `waiting` or `picked`) and `picked_attempt`, and each run its `attempt`.",
15201522 access: "write",
15211523 },
1524+ "GET /repos/{owner}/{repo}/diffinery/tasks/{number}/attempts": {
1525+ summary: "Compare a task's attempts",
1526+ description: "Needs write access. One entry per attempt of a best-of-N task: `attempt`, `run`, `state`, `error`, `branch`, `head_sha`, `changes` (`files`, `additions`, `deletions`), `checks` and `failing_checks` on its branch, `model_requests`, `tokens`, `summary` (its last message) and `compare_url`. See /docs/agents/tasks#best-of-n.",
1527+ access: "write",
1528+ },
1529+ "POST /repos/{owner}/{repo}/diffinery/tasks/{number}/attempts/{attempt}/pick": {
1530+ summary: "Pick a task's attempt",
1531+ description: "From a browser session only, by the person who started the task. The attempt's branch becomes the task's branch, with a pull request; the other attempts' branches are deleted. 409 `the attempts are still running`, `attempt <n> is already picked` or `this task has no attempts to pick from`; 404 `no such attempt`; 422 `attempt <n> has no work to pick: ...` when it changed nothing or didn't finish. Audited as `task.pick`.",
1532+ access: "write",
1533+ status: 202,
1534+ },
15221535 "POST /repos/{owner}/{repo}/diffinery/tasks/{number}/plan/approve": {
15231536 summary: "Approve a task's plan",
15241537 description: "From a browser session only, by the person who started the task. The build run starts with the goal and the plan, and every later run gets the plan as the approved one. 409 `the plan isn't ready yet` while it plans, `this task has no plan waiting for approval`, or `the task is running`. Audited as `task.plan_approve`. See /docs/agents/tasks#plan-first.",