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: plan first in the API reference, audit log and limits (#107)

#104 documented plan-first tasks on the Tasks and Projects pages. The
API reference had no entries for approving or splitting a plan, nor
plan_first on starting a task or in the project settings; the audit log
lacked task.plan_approve and task.plan_split; and Limits had no plan
length.


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

Co-authored-by: Claude <noreply@anthropic.com>
Canberk Demirci authored · GitHub committed signed1 parent e04062dcommit 2abccfde72561801a101ff45b9f3820e2aae2e60Browse files
3 files changed+31−5
modifiedapps/diffinery/docs/reference/limits.md+1−0View file
@@ -235,6 +235,7 @@
235235 | Run length | 6 hours | A run still going after 6 hours is stopped and fails with `The run took longer than 6 hours and was stopped.` |
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). |
238+| 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)`. |
238239 | Custom command length | 2,000 characters | ``422 `command` is longer than 2000 characters``. |
239240 | 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. |
240241 | Transcript lines per MCP call | 500 | `agent_task` returns at most 500 lines after `after`. |
modifiedapps/diffinery/docs/security/audit-log.md+4−2View file
@@ -74,11 +74,13 @@
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` |
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 |
7878 | `task.message`, `task.retry` | The person who started a task sends a follow-up or retries it | `owner/repo#<task number>` | none |
79+| `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.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` |
7981 | `task.stop` | Someone stops a task (its starter, its agent or a repository admin) | `owner/repo#<task number>` | none |
8082 | `project.memory` | Someone saves or restores the [project](/docs/agents/projects) memory | `owner/repo` | `version`, `length`, and `restored` (the version restored) for a restore |
81-| `project.settings` | A repository admin changes **Settings → Project** | `owner/repo` | `max_running`, `instructions_length` |
83+| `project.settings` | A repository admin changes **Settings → Project** | `owner/repo` | `max_running`, `instructions_length`, `plan_first` |
8284 | `project.coordinator` | Someone turns their [coordinator](/docs/agents/projects#coordinator) on, changes it or turns it off | `owner/repo` | `enabled`, and when on `agent`, `runtime`, `provider` and `model` (OpenRouter), `tokens_per_run`, `tokens_per_task` |
8385 | `project.coordinate` | Someone's coordinator is given a post to handle | `owner/repo` | `post`, `task`, `agent`, `runtime` |
8486 | `review.settings` | A repository admin turns [agent review](/docs/agents/review) on or off, or changes its settings | `owner/repo` | `enabled`, `agent`, `runtime`, `trigger`, `autofix`, `daily_limit` |
modifiedapps/diffinery/src/docs/api-meta.ts+26−3View file
@@ -62,6 +62,8 @@
6262 "POST /repos/{owner}/{repo}/diffinery/tasks": { scope: "write", sessionOnly: true },
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 },
65+ "POST /repos/{owner}/{repo}/diffinery/tasks/{number}/plan/approve": { scope: "write", sessionOnly: true },
66+ "POST /repos/{owner}/{repo}/diffinery/tasks/{number}/plan/split": { scope: "write", sessionOnly: true },
6567 "POST /repos/{owner}/{repo}/diffinery/project/posts": { scope: "write", sessionOnly: true },
6668 "PUT /repos/{owner}/{repo}/diffinery/project/memory": { scope: "write", sessionOnly: true },
6769 "PUT /repos/{owner}/{repo}/diffinery/project/settings": { scope: "write", sessionOnly: true },
@@ -1443,6 +1445,7 @@
14431445 model: ["string", "With `provider: openrouter`: the OpenRouter model id (default `~anthropic/claude-sonnet-latest` for Claude Code, `~openai/gpt-sol-latest` for Codex)."],
14441446 agent: ["string", "The agent's login (required)."],
14451447 base: ["string", "Branch to start from (default: the default branch)."],
1448+ 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."],
14461449 command: ["string", "For `custom`: the shell command to run."],
14471450 },
14481451 },
@@ -1503,11 +1506,31 @@
15031506 },
15041507 "PUT /repos/{owner}/{repo}/diffinery/project/settings": {
15051508 summary: "Change the project settings",
1506- description: "Project instructions every agent task and agent review gets, and how many tasks run at once. Tasks beyond `max_running` queue and start oldest first. A person in a browser session only. Audited as `project.settings`.",
1509+ description: "Project instructions every agent task and agent review gets, how many tasks run at once, and whether tasks plan first. Tasks beyond `max_running` queue and start oldest first. A person in a browser session only. Audited as `project.settings`.",
15071510 access: "admin",
1508- body: { max_running: ["integer", "1 to 10 (default 3)."], instructions: ["string", "At most 10,000 characters."] },
1511+ body: {
1512+ max_running: ["integer", "1 to 10 (default 3)."],
1513+ instructions: ["string", "At most 10,000 characters."],
1514+ plan_first: ["string", "`off` (default: tasks build at once unless started with `plan_first: true`), `default` (tasks plan first unless started with `plan_first: false`) or `required` (every task plans first, including ones coordinators start)."],
1515+ },
15091516 },
1510- "GET /repos/{owner}/{repo}/diffinery/tasks/{number}": { summary: "Get an agent task", description: "The task, its runs and its pull request. Needs write access.", access: "write" },
1517+ "GET /repos/{owner}/{repo}/diffinery/tasks/{number}": {
1518+ 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`.",
1520+ access: "write",
1521+ },
1522+ "POST /repos/{owner}/{repo}/diffinery/tasks/{number}/plan/approve": {
1523+ summary: "Approve a task's plan",
1524+ 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.",
1525+ access: "write",
1526+ status: 202,
1527+ },
1528+ "POST /repos/{owner}/{repo}/diffinery/tasks/{number}/plan/split": {
1529+ summary: "Split a task's plan into tasks",
1530+ description: "From a browser session only, by the person who started the task. Your coordinator gets the goal and the plan as a post of yours and starts tasks for its parts, each with `parent_task`; this task is then done with. Returns `post`, `task` (the coordinator's run) and `status` (`started` or `queued`). 422 `set up your coordinator for this project first` without a coordinator; the same 409s as approving. Audited as `task.plan_split`.",
1531+ access: "write",
1532+ status: 202,
1533+ },
15111534 "GET /repos/{owner}/{repo}/diffinery/tasks/{number}/transcript": {
15121535 summary: "Read a task's transcript",
15131536 description: "Lines after `after` (a line number), oldest first: `stream` is `out` and `err` (the runtime's output, Claude Code's `stream-json` or Codex's `--json` lines), `setup` (installing and pushing) or `note` (Diffinery's own). Model keys and git credentials are masked.",