openvibers/openvibe.codes Commits

Per-turn checkpoint refs and a turn diff (#372)

OP OpenVibers committed 3 days ago · 7f09136d29 · 10 files, +490 −114

* Per-turn checkpoint refs and a turn diff

* Drop the duplicate runtime test the rename left behind

Changes

ddiff --git a/docs/checkpoints.md b/docs/checkpoints.md
nnew file mode 100644
iindex 0000000..de0f28b
---- /dev/null
++++ b/docs/checkpoints.md
@@@ -0,0 +1,55 @@
++# Per-turn checkpoints
++
++A coding job clones the repository into a scratch directory and an agent works there. `harness/checkpoints.js` pins
++that worktree to a git commit before each tool batch and keeps it under a ref:
++
++```
++refs/codes/checkpoints/<task>/<run>/<n>
++```
++
++The refs are the run's own history. They are never pushed and never become a branch, and the clone (and so the refs)
++is removed when the job ends. What survives is the job result: the refs, each turn's diff, and the final turn diff
++against the clone as the agent found it.
++
++## How a checkpoint is written
++
++A checkpoint uses a temporary index (`GIT_INDEX_FILE`) so it never touches the run's own index:
++
++```
++read-tree HEAD → add -A → write-tree → commit-tree -p <previous checkpoint> → update-ref
++```
++
++`.gitignore` and `.git/info/exclude` still apply, so `node_modules`, build output and the owner's `.openvibe/` files
++stay out. `n = 0` is the clone as the agent found it; the job takes one more at the end (`final`) for the last turn.
++
++## The turn diff
++
++`checkpoints.turns[]` has one row per checkpoint after the start:
++
++```json
++{
++ "n": 2, "ref": "refs/codes/checkpoints/tsk_…/run_…/2", "label": "turn 1",
++ "from": 1, "to": 2, "files_total": 3, "authored": 2, "imported": 1, "truncated": false,
++ "files": [
++ { "path": "src/user.js", "status": "M", "added": 4, "removed": 3, "authored": true },
++ { "path": "package-lock.json", "status": "M", "added": 120, "removed": 2, "authored": false }
++ ]
++}
++```
++
++A file is **authored** when a tool call named it during the run (Claude Code's tool vocabulary, whatever harness ran)
++and **imported** otherwise: a patch applied from an earlier conversation, a dependency install, a formatter, a
++subagent's work. `status` is git's letter (`A`, `M`, `D`, `R`); `checkpoints.turn_diff` is the whole run against
++`n = 0`. A result carries at most 20 turns and 20 files a turn (`files_total` says how many there really were).
++
++## Restoring
++
++`createCheckpoints(...).restore(n)` puts the worktree back to a checkpoint: `read-tree --reset -u` of its tree, then
++`git clean -fdq` for the untracked files a broken turn left (ignored files stay). The last checkpoint is the last good
++state before the current turn. This is the operation a **failed repair** uses: the repair loop (After-Loops AL2, the
++same builder resumed with findings) restores the last good turn before it retries, rather than asking the model to
++re-read and re-edit from a broken tree. Routine hand-offs do **not** restore: when an agent runs out of its plan
++mid-work, the next agent keeps that work (the hand-off note tells it what was done).
++
++The module is tested in `test/checkpoints.test.js`; a job's integration (refs written, the agent's own file marked
++authored, the failed run still carrying its diffs) runs in the same file against a real local clone.
ddiff --git a/docs/harness-runtime.md b/docs/harness-runtime.md
iindex 6873ac0..7d05398 100644
---- a/docs/harness-runtime.md
++++ b/docs/harness-runtime.md
@@@ -35,19 +35,20 @@ nothing, and is excluded only when a requirement is set.
| harness | terminal_status | steering | checkpointing | subagents | context_handoff | enforcement |
|---|---|---|---|---|---|---|
--| `claude-code` | structured | resume | none | native | session | native |
--| `claude-other` | structured | resume | none | native | session | native |
--| `codex` | structured | resume | none | none | session | native |
--| `opencode` | structured | resume | none | none | session | client-boundary |
--| `command-code` | structured | resume | none | none | session | client-boundary |
--| `aider` | parsed | none | none | none | none | client-boundary |
--| `minimax-code` | structured | resume | none | none | session | native |
--| `deepseek` | structured | resume | none | none | session | client-boundary |
--| `openvibe-ai` | structured | resume | none | none | session | client-boundary |
--| `openai-compatible` | structured | resume | none | none | session | client-boundary |
++| `claude-code` | structured | resume | git | native | session | native |
++| `claude-other` | structured | resume | git | native | session | native |
++| `codex` | structured | resume | git | none | session | native |
++| `opencode` | structured | resume | git | none | session | client-boundary |
++| `command-code` | structured | resume | git | none | session | client-boundary |
++| `aider` | parsed | none | git | none | none | client-boundary |
++| `minimax-code` | structured | resume | git | none | session | native |
++| `deepseek` | structured | resume | git | none | session | client-boundary |
++| `openvibe-ai` | structured | resume | git | none | session | client-boundary |
++| `openai-compatible` | structured | resume | git | none | session | client-boundary |
--`checkpointing` flips to `git` on every row when the per-turn checkpoint refs land (`harness/job.js`): the manifest is
--the runtime's declaration, so it must not claim a checkpoint before one exists.
++`checkpointing` is `git` on every row: a job pins its clone to `refs/codes/checkpoints/<task>/<run>/<n>` per turn
++(`harness/job.js`, `harness/checkpoints.js`) and can restore the last good one; the refs are the run's own and are
++never pushed.
## Using it
ddiff --git a/engine/runtime.js b/engine/runtime.js
iindex 64518a6..e3188bf 100644
---- a/engine/runtime.js
++++ b/engine/runtime.js
@@@ -65,48 +65,49 @@ const CAPABILITY = Object.freeze({
*
* Grounded in each adapter (harness/adapters/cli.js, harness/adapters/api.js) and its CLI's flags, not in its name:
* every CLI here ends with a result line except Aider (its prose is parsed); every CLI with `--resume`/`--session`/`-s`
-- * continues its session; Claude Code alone exposes subagents as tools. `checkpointing` is `none` until the per-turn
-- * checkpoint refs land: this manifest is the runtime's declaration, so it must not claim one early.
++ * continues its session; Claude Code alone exposes subagents as tools. `checkpointing` is `git` for every harness:
++ * the job pins its clone to `refs/codes/checkpoints/<task>/<run>/<n>` per turn (harness/checkpoints.js), whatever the
++ * harness.
*/
const MANIFEST = Object.freeze({
'claude-code': Object.freeze({
-- terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
++ terminal_status: 'structured', steering: 'resume', checkpointing: 'git',
subagents: 'native', context_handoff: 'session', enforcement: 'native',
}),
'claude-other': Object.freeze({
-- terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
++ terminal_status: 'structured', steering: 'resume', checkpointing: 'git',
subagents: 'native', context_handoff: 'session', enforcement: 'native',
}),
codex: Object.freeze({
-- terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
++ terminal_status: 'structured', steering: 'resume', checkpointing: 'git',
subagents: 'none', context_handoff: 'session', enforcement: 'native',
}),
opencode: Object.freeze({
-- terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
++ terminal_status: 'structured', steering: 'resume', checkpointing: 'git',
subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',
}),
'command-code': Object.freeze({
-- terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
++ terminal_status: 'structured', steering: 'resume', checkpointing: 'git',
subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',
}),
aider: Object.freeze({
-- terminal_status: 'parsed', steering: 'none', checkpointing: 'none',
++ terminal_status: 'parsed', steering: 'none', checkpointing: 'git',
subagents: 'none', context_handoff: 'none', enforcement: 'client-boundary',
}),
'minimax-code': Object.freeze({
-- terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
++ terminal_status: 'structured', steering: 'resume', checkpointing: 'git',
subagents: 'none', context_handoff: 'session', enforcement: 'native',
}),
deepseek: Object.freeze({
-- terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
++ terminal_status: 'structured', steering: 'resume', checkpointing: 'git',
subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',
}),
'openvibe-ai': Object.freeze({
-- terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
++ terminal_status: 'structured', steering: 'resume', checkpointing: 'git',
subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',
}),
'openai-compatible': Object.freeze({
-- terminal_status: 'structured', steering: 'resume', checkpointing: 'none',
++ terminal_status: 'structured', steering: 'resume', checkpointing: 'git',
subagents: 'none', context_handoff: 'session', enforcement: 'client-boundary',
}),
});
ddiff --git a/harness/checkpoints.js b/harness/checkpoints.js
nnew file mode 100644
iindex 0000000..7c339a5
---- /dev/null
++++ b/harness/checkpoints.js
@@@ -0,0 +1,209 @@
++'use strict';
++
++/**
++ * Per-turn checkpoints in a job's clone: before each model turn (really before each tool batch, which is the closest
++ * boundary the stream shows) the working tree is written to a git commit and pinned under
++ * `refs/codes/checkpoints/<task>/<run>/<n>`. Nothing is committed to a branch and nothing is pushed: the refs are the
++ * run's own history, and the job's clone is thrown away after it.
++ *
++ * const cp = createCheckpoints({ cwd, task, run });
++ * cp.capture('start'); // n = 0, the clone as the agent found it
++ * cp.authored(['src/user.js']); // paths this turn's tools wrote
++ * cp.capture('turn'); // n = 1, after the turn
++ * cp.diff(0, 1); // { files: [{ path, status, added, removed, authored }], … }
++ * cp.restore(0); // put the worktree back to the last good turn (the repair path)
++ *
++ * The diff marks a path **authored** when a tool call named it in the turn and **imported** otherwise (a patch applied,
++ * a dependency install, a formatter or a subagent): the first is the agent's work, the second is something that came
++ * from elsewhere, and a review should be told them apart.
++ *
++ * A checkpoint is built with a temporary index (`GIT_INDEX_FILE`) so it never touches the run's own index:
++ * `read-tree HEAD` → `add -A` → `write-tree` → `commit-tree` with the previous checkpoint as parent → `update-ref`.
++ * `.gitignore` and `.git/info/exclude` still apply, so build output, node_modules and the owner's `.openvibe/` files
++ * stay out.
++ */
++
++const fs = require('fs');
++const os = require('os');
++const path = require('path');
++const { execFileSync } = require('child_process');
++
++/** A cap on a run's refs and on a diff's rows, so a very long turn cannot flood the result. */
++const MAX_TURNS = 200;
++const MAX_FILES = 100;
++
++/** git in the worktree, no prompt and no credential helper; a failure names the command and the last line. */
++function git(cwd, args, env = {}) {
++ try {
++ return execFileSync('git', args, {
++ cwd, encoding: 'utf8', timeout: 30_000, maxBuffer: 16 * 1024 * 1024,
++ env: { ...process.env, GIT_TERMINAL_PROMPT: '0', GIT_ASKPASS: 'echo', ...env },
++ stdio: ['ignore', 'pipe', 'pipe'],
++ });
++ } catch (error) {
++ const said = String(error.stderr || error.message || '').trim().split('\n').slice(-2).join(' ');
++ const err = new Error(`checkpoint: git ${args[0]} failed: ${said}`);
++ err.code = 'checkpoint.git';
++ throw err;
++ }
++}
++
++/** A ref path component that git accepts and that a person can read back (a task id is already safe). */
++function safeName(value) {
++ return String(value == null ? '' : value).replace(/[^A-Za-z0-9._-]/g, '_').slice(0, 80) || 'run';
++}
++
++/** The file paths a harness event wrote, from its tool calls (Claude Code's names are the shared vocabulary). */
++function toolPaths(event) {
++ const out = [];
++ if (!event || event.type !== 'assistant') return out;
++ for (const c of (event.message && event.message.content) || []) {
++ if (!c || c.type !== 'tool_use') continue;
++ const i = c.input && typeof c.input === 'object' ? c.input : {};
++ for (const key of ['file_path', 'path', 'notebook_path']) if (typeof i[key] === 'string' && i[key]) out.push(i[key]);
++ // A patch helper names its files in a list; an edit may carry a list of edits for one path.
++ if (Array.isArray(i.files)) out.push(...i.files.filter((f) => typeof f === 'string'));
++ }
++ return out;
++}
++
++/**
++ * The checkpoints of one run.
++ *
++ * @param {object} opts
++ * @param {string} opts.cwd the clone
++ * @param {string} [opts.task] the task id (the ref's first component)
++ * @param {string} [opts.run] the run id (the ref's second component)
++ * @param {number} [opts.maxTurns] the most refs one run writes
++ * @param {number} [opts.maxFiles] the most rows one diff carries
++ * @param {() => number} [opts.now]
++ */
++function createCheckpoints({ cwd, task = 'task', run = 'run', maxTurns = MAX_TURNS, maxFiles = MAX_FILES, now = Date.now } = {}) {
++ const root = `refs/codes/checkpoints/${safeName(task)}/${safeName(run)}`;
++ const entries = [];
++ const pending = new Set();
++ // Every path a tool has named in this run: a diff marks a file authored if the agent touched it, however many
++ // turns the write and the event that reports it fall apart by (a CLI prints the call before or after it runs).
++ const named = new Set();
++
++ const head = () => git(cwd, ['rev-parse', 'HEAD']).trim();
++
++ /**
++ * Capture the worktree now as checkpoint `n`. The parent is the previous checkpoint (or HEAD for the first), so the
++ * chain is inspectable with `git log`. The paths tools wrote since the previous capture are this checkpoint's
++ * authored set.
++ *
++ * @param {string} [label] what the capture marks (start, turn, final)
++ * @returns {object|null} the entry, or null once the cap is reached
++ */
++ function capture(label = 'turn') {
++ if (entries.length >= maxTurns) return null;
++ const n = entries.length;
++ const index = path.join(os.tmpdir(), `codes-index-${process.pid}-${Date.now()}-${n}`);
++ const env = { GIT_INDEX_FILE: index };
++ try {
++ git(cwd, ['read-tree', 'HEAD'], env);
++ git(cwd, ['add', '-A'], env);
++ const tree = git(cwd, ['write-tree'], env).trim();
++ const parent = entries.length ? entries[entries.length - 1].sha : head();
++ const sha = git(cwd, [
++ '-c', 'user.name=OpenVibe.Codes', '-c', '[email protected]',
++ 'commit-tree', tree, '-p', parent, '-m', `codes checkpoint ${n} ${label}`,
++ ]).trim();
++ const ref = `${root}/${n}`;
++ git(cwd, ['update-ref', ref, sha]);
++ const entry = { n, ref, sha, tree, label, at: new Date(now()).toISOString(), authored: [...pending] };
++ pending.clear();
++ entries.push(entry);
++ return entry;
++ } finally {
++ try { fs.rmSync(index, { force: true }); } catch { /* the temp index is best effort */ }
++ }
++ }
++
++ /** The paths the current turn's tools wrote: a per-turn snapshot, and the run's cumulative authored set. */
++ function authored(paths) {
++ for (const p of paths || []) if (p) { pending.add(String(p)); named.add(String(p)); }
++ }
++
++ /** Resolve a checkpoint by number, ref or sha. */
++ function entryOf(which) {
++ if (which && typeof which === 'object') return which;
++ return entries.find((e) => e.n === Number(which) || e.ref === which || e.sha === which) || null;
++ }
++
++ /**
++ * The diff one turn made, with its paths split by provenance.
++ *
++ * @param {number|string|object} from checkpoint n / ref / sha
++ * @param {number|string|object} to checkpoint n / ref / sha
++ * @returns {{from: number|null, to: number|null, files: object[], files_total: number, authored: number, imported: number}}
++ */
++ function diff(from, to) {
++ const a = entryOf(from);
++ const b = entryOf(to);
++ const empty = { from: a ? a.n : null, to: b ? b.n : null, files: [], files_total: 0, authored: 0, imported: 0 };
++ if (!a || !b) return empty;
++ const status = git(cwd, ['diff', '--name-status', '--find-renames', a.sha, b.sha]).trim();
++ if (!status) return empty;
++ const stats = new Map();
++ for (const row of git(cwd, ['diff', '--numstat', a.sha, b.sha]).trim().split('\n').filter(Boolean)) {
++ const [added, removed, p] = row.split('\t');
++ if (p == null) continue;
++ // A rename's numstat path is `old => new` (or `{old => new}` for a shared prefix): keep the newest path.
++ const file = p.includes(' => ') ? p.replace(/^.* => /, '').replace(/[{}]/g, '').trim() : p;
++ stats.set(file, { added: Number(added) || 0, removed: Number(removed) || 0 });
++ }
++ const authoredSet = named;
++ const files = [];
++ for (const line of status.split('\n')) {
++ const parts = line.split('\t');
++ const code = String(parts[0] || '').charAt(0);
++ const file = code === 'R' || code === 'C' ? parts[2] : parts[1];
++ if (!file) continue;
++ const nums = stats.get(file) || { added: 0, removed: 0 };
++ files.push({ path: file, status: code, added: nums.added, removed: nums.removed, authored: authoredSet.has(file) || (code === 'R' && authoredSet.has(parts[1])) });
++ }
++ const authored = files.filter((f) => f.authored).length;
++ return { from: a.n, to: b.n, files: files.slice(0, maxFiles), files_total: files.length, authored, imported: files.length - authored };
++ }
++
++ /**
++ * Put the worktree back to a checkpoint: index and tracked files from its tree, then the untracked files a broken
++ * turn left are cleaned (ignored files, including `.openvibe/`, stay).
++ *
++ * @param {number|string|object} which checkpoint n / ref / sha
++ * @returns {{n: number, ref: string, sha: string}|null}
++ */
++ function restore(which) {
++ const entry = entryOf(which);
++ if (!entry) return null;
++ git(cwd, ['read-tree', '--reset', '-u', entry.sha]);
++ try { git(cwd, ['clean', '-fdq']); } catch { /* a clean failure leaves the restored tree as is */ }
++ return { n: entry.n, ref: entry.ref, sha: entry.sha };
++ }
++
++ /** The refs written, oldest first, without the paths (a result carries the diffs separately). */
++ function list() {
++ return entries.map((e) => ({ n: e.n, ref: e.ref, sha: e.sha, label: e.label, at: e.at }));
++ }
++
++ /** The last checkpoint (the last good state before the current turn). */
++ function last() {
++ return entries[entries.length - 1] || null;
++ }
++
++ /** Every turn's diff from the previous checkpoint, one row per ref after the first. */
++ function turns() {
++ const out = [];
++ for (let i = 1; i < entries.length; i++) {
++ const d = diff(entries[i - 1], entries[i]);
++ out.push({ n: entries[i].n, ref: entries[i].ref, sha: entries[i].sha, label: entries[i].label, at: entries[i].at, ...d });
++ }
++ return out;
++ }
++
++ return { root, capture, authored, diff, restore, list, last, turns, entries, safeName };
++}
++
++module.exports = { createCheckpoints, toolPaths, safeName, MAX_TURNS, MAX_FILES };
ddiff --git a/harness/job.js b/harness/job.js
iindex e0c38c7..5a0fff2 100644
---- a/harness/job.js
++++ b/harness/job.js
@@@ -9,15 +9,17 @@
*
* It clones `repo` (https only) into a scratch directory, runs the harness there with hand-offs, and prints one compact
* JSON line per event on stdout (what the page shows live). The result goes to fd 3 (the Link's result channel), or as
-- * a last `{ t: 'result_doc' }` line when fd 3 is not open: what the agents said, the changed files and the patch, and,
-- * when `push` is asked and the machine holds GITHUB_TOKEN, the new branch it pushed (never the base branch) and its
-- * pull request. Nothing here ever prints a token.
++ * a last `{ t: 'result_doc' }` line when fd 3 is not open: what the agents said, the changed files and the patch, the
++ * per-turn checkpoints (refs and the authored/imported turn diff, `checkpoints` in the document), and, when `push` is
++ * asked and the machine holds GITHUB_TOKEN, the new branch it pushed (never the base branch) and its pull request.
++ * Nothing here ever prints a token.
*/
const fs = require('fs');
const os = require('os');
const path = require('path');
const { execFile } = require('child_process');
const { collectProof } = require('../engine/proof');
++const { createCheckpoints, toolPaths } = require('./checkpoints');
const JOB_PROTOCOL = '1.0.0';
/** The agents a conductor's turn may run on: every harness Codes offers. */
@@@ -248,6 +250,8 @@ function parseArgs(raw, env = process.env) {
} : null;
return {
kind, repo: repo ? repo.toString() : null, ref, prompt, permission, push,
++ // The task this job is an attempt of, when the server names it: the checkpoint refs carry it.
++ ...(typeof a.task_id === 'string' && /^tsk_[0-9A-HJKMNP-TV-Z]{26}$/.test(a.task_id) ? { taskId: a.task_id } : {}),
...(attached.length ? { attachments: attached } : {}),
...(limits ? { limits } : {}),
...(resume ? { resume } : {}),
@@@ -782,11 +786,34 @@ async function runJob({ input, harness, emit, writeResult, env = process.env, tm
resume: job.resume || null,
...(adapterOptions ? { adapterOptions } : {}),
});
++ // Per-turn checkpoints (Release 1, feat 11): the clone as the agent found it, then one ref per tool batch. They
++ // live in this clone only and are never pushed; the result carries the refs and the turn diffs so a review can
++ // see the agent's own work apart from what came from elsewhere. A checkpoint failure never fails the run.
++ let checkpoints = null;
++ if (job.kind === 'code') {
++ try {
++ checkpoints = createCheckpoints({ cwd: work, task: job.taskId || 'task', run: env.OPENVIBE_JOB_ID || run.runId });
++ checkpoints.capture('start');
++ } catch (e) {
++ say({ t: 'note', level: 'warning', text: `Checkpoints are off for this run: ${scrub(e.message)}` });
++ checkpoints = null;
++ }
++ }
let last = null;
// What the pictures may take of the result's room.
const pictures = { left: MAX_RESULT_BYTES };
for await (const e of run) {
last = e;
++ // Before the tool batch the model just called runs, pin the tree: this is the checkpoint a failed repair
++ // returns to. The paths the batch names become the authored set of the next checkpoint's diff.
++ if (checkpoints && e.type === 'assistant' && ((e.message && e.message.content) || []).some((c) => c && c.type === 'tool_use')) {
++ try {
++ checkpoints.authored(toolPaths(e));
++ checkpoints.capture(`turn ${e._attempt || 1}`);
++ } catch (err) {
++ say({ t: 'note', level: 'warning', text: `A checkpoint could not be written: ${scrub(err.message)}` });
++ }
++ }
const c = compact(e, { pictures });
for (const line of Array.isArray(c) ? c : c ? [c] : []) say(line);
if (e.type === 'system' && e.subtype === 'init' && e.agent && !result.agents.includes(e.agent)) result.agents.push(e.agent);
@@@ -806,6 +833,24 @@ async function runJob({ input, harness, emit, writeResult, env = process.env, tm
if (job.kind === 'conductor' && last && last.session_id) result.session = String(last.session_id).slice(0, 200);
// A conductor works in the owner's own folder: its changes are theirs, not a patch for a pull request.
Object.assign(result, job.kind === 'code' ? await changes(work) : { files: [], patch: '', patch_truncated: false });
++ if (checkpoints) {
++ try {
++ checkpoints.capture('final');
++ const turns = checkpoints.turns();
++ const baseline = checkpoints.entries[0];
++ const final = checkpoints.last();
++ result.checkpoints = {
++ root: checkpoints.root,
++ count: checkpoints.list().length,
++ refs: checkpoints.list().slice(-20),
++ // The turn diff: one row per turn, with the files the turn wrote split into authored and imported.
++ turns: turns.slice(-20).map((t) => ({ ...t, files: t.files.slice(0, 20), truncated: t.files_total > 20 })),
++ ...(baseline && final ? { turn_diff: checkpoints.diff(baseline, final) } : {}),
++ };
++ } catch (e) {
++ say({ t: 'note', level: 'warning', text: `The checkpoints could not be read back: ${scrub(e.message)}` });
++ }
++ }
say({ t: 'step', text: result.files.length ? `${result.files.length} file${result.files.length === 1 ? '' : 's'} changed` : (job.kind === 'conductor' ? 'Turn done' : 'No files changed') });
// The proof of the change: the tests it reaches, the pages it touched, the diff, and a second model's opinion.
// It is collected here, in the clone the agent worked in, so the evidence is about this machine's work.
ddiff --git a/server/domain/run-tasks.js b/server/domain/run-tasks.js
iindex c941043..7ed3998 100644
---- a/server/domain/run-tasks.js
++++ b/server/domain/run-tasks.js
@@@ -588,9 +588,13 @@ function createRunTasks({ store, run, config, log = console, sessions = null, ou
}
// The job gets the files and the lines naming them, after the knowledge recall so it is in the same prompt;
// the task row keeps the person's own words, so the page's "The brief" does not repeat the paths.
-- const jobArgs = claims.length
-- ? { ...runArgs, attachments: [...(Array.isArray(runArgs.attachments) ? runArgs.attachments : []), ...fileArgs(claims, turnId)], prompt: withFiles(runArgs.prompt, claims, turnId) }
-- : runArgs;
++ const jobArgs = {
++ ...(claims.length
++ ? { ...runArgs, attachments: [...(Array.isArray(runArgs.attachments) ? runArgs.attachments : []), ...fileArgs(claims, turnId)], prompt: withFiles(runArgs.prompt, claims, turnId) }
++ : runArgs),
++ // The harness's per-turn checkpoint refs name the task they belong to: refs/codes/checkpoints/<task>/<run>/<n>
++ task_id: id,
++ };
const actorRefValue = actorRef && typeof actorRef === 'object' && !Array.isArray(actorRef) ? JSON.stringify(actorRef) : null;
// Placement A1: the task's kind, its harness, and why the placer put it there. The person's rules, Bench priors
// and learned counts are read once here; the placement itself is pure. A task whose task kind is review is
ddiff --git a/test/checkpoints.test.js b/test/checkpoints.test.js
nnew file mode 100644
iindex 0000000..f7011e4
---- /dev/null
++++ b/test/checkpoints.test.js
@@@ -0,0 +1,138 @@
++'use strict';
++/**
++ * Per-turn checkpoints (harness/checkpoints.js, harness/job.js): a job's clone is pinned under
++ * `refs/codes/checkpoints/<task>/<run>/<n>` before each tool batch, the turn diff splits the files the agent wrote
++ * (authored) from the ones that came from elsewhere (imported), and a checkpoint can be restored. The job result
++ * carries the refs and the diffs, never a push of them.
++ */
++const assert = require('assert');
++const fs = require('fs');
++const os = require('os');
++const path = require('path');
++const { execFileSync } = require('child_process');
++const { pathToFileURL } = require('url');
++const { createCheckpoints, toolPaths } = require('../harness/checkpoints');
++const { createHarness } = require('../harness');
++const { runJob } = require('../harness/job');
++const { check, done } = require('./helpers/boot');
++
++const git = (cwd, args) => execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
++
++(async () => {
++ await check('a checkpoint is a git ref; the turn diff splits authored paths from imported ones', () => {
++ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'codes-cp-'));
++ const repo = path.join(dir, 'repo');
++ fs.mkdirSync(repo);
++ git(repo, ['init', '-q']);
++ fs.writeFileSync(path.join(repo, 'a.js'), 'const a = 1;\n');
++ git(repo, ['add', '-A']);
++ git(repo, ['-c', 'user.name=t', '-c', 'user.email=t@t', 'commit', '-qm', 'base']);
++ try {
++ const cp = createCheckpoints({ cwd: repo, task: 'tsk_test', run: 'run_test' });
++ const zero = cp.capture('start');
++ assert.strictEqual(zero.n, 0);
++ assert.strictEqual(zero.ref, 'refs/codes/checkpoints/tsk_test/run_test/0');
++ assert.strictEqual(git(repo, ['rev-parse', '--verify', zero.ref]), zero.sha);
++ assert.deepStrictEqual(cp.list().map((e) => e.n), [0]);
++
++ fs.writeFileSync(path.join(repo, 'a.js'), 'const a = 2;\n');
++ cp.authored(['a.js']);
++ const one = cp.capture('turn 1');
++ fs.writeFileSync(path.join(repo, 'b.js'), 'imported\n');
++ const two = cp.capture('turn 2');
++
++ const first = cp.diff(zero, one);
++ assert.deepStrictEqual(first.files, [{ path: 'a.js', status: 'M', added: 1, removed: 1, authored: true }]);
++ assert.deepStrictEqual(cp.diff(one, two).files, [{ path: 'b.js', status: 'A', added: 1, removed: 0, authored: false }]);
++ assert.deepStrictEqual(cp.turns().map((t) => t.n), [1, 2]);
++ assert.strictEqual(cp.turns()[1].authored, 0);
++ assert.strictEqual(cp.turns()[1].imported, 1);
++
++ // Restore the last good checkpoint: a.js back, the imported file and an untracked broken one gone.
++ fs.writeFileSync(path.join(repo, 'junk.js'), 'broken\n');
++ assert.deepStrictEqual(cp.restore(zero), { n: 0, ref: zero.ref, sha: zero.sha });
++ assert.strictEqual(fs.readFileSync(path.join(repo, 'a.js'), 'utf8'), 'const a = 1;\n');
++ assert.strictEqual(fs.existsSync(path.join(repo, 'b.js')), false);
++ assert.strictEqual(fs.existsSync(path.join(repo, 'junk.js')), false);
++ assert.strictEqual(cp.restore(99), null);
++ } finally {
++ fs.rmSync(dir, { recursive: true, force: true });
++ }
++ });
++
++ await check('toolPaths reads the file a tool call names, never a command', () => {
++ assert.deepStrictEqual(toolPaths({ type: 'assistant', message: { content: [
++ { type: 'tool_use', input: { file_path: 'src/a.js' } },
++ { type: 'tool_use', input: { path: 'src/b.js' } },
++ { type: 'tool_use', input: { command: 'ls' } },
++ { type: 'tool_use', input: { files: ['src/c.js', 7] } },
++ ] } }), ['src/a.js', 'src/b.js', 'src/c.js']);
++ assert.deepStrictEqual(toolPaths({ type: 'user', message: { content: [{ type: 'tool_result' }] } }), []);
++ });
++
++ await check('a job carries its checkpoint refs and the final turn diff; the agent\'s own file is authored', async () => {
++ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'codes-cp-job-'));
++ const repo = path.join(dir, 'repo');
++ const bin = path.join(dir, 'bin');
++ const home = path.join(dir, 'home');
++ fs.mkdirSync(repo); fs.mkdirSync(bin);
++ fs.writeFileSync(path.join(repo, 'a.js'), 'const a = 1;\n');
++ git(repo, ['init', '-q']);
++ git(repo, ['add', '-A']);
++ git(repo, ['-c', 'user.name=t', '-c', 'user.email=t@t', 'commit', '-qm', 'base']);
++
++ // Codex writes a file (it prints the file_change after writing, as the real CLI does), then hits its limit.
++ // Claude Code takes over and finishes: the note is the agent's own work, not an import, whichever attempt did it.
++ fs.writeFileSync(path.join(bin, 'codex'), `#!${process.execPath}
++'use strict';
++const fs = require('fs');
++const out = (o) => console.log(JSON.stringify(o));
++out({ type: 'thread.started', thread_id: 'cx-cp' });
++out({ type: 'turn.started' });
++fs.mkdirSync('notes', { recursive: true });
++fs.writeFileSync('notes/turn.txt', 'written by codex\\n');
++out({ type: 'item.completed', item: { id: 'i2', type: 'file_change', changes: [{ path: 'notes/turn.txt', kind: 'add' }], status: 'completed' } });
++out({ type: 'error', message: 'You have hit your usage limit.' });
++out({ type: 'turn.failed', error: { message: 'You have hit your usage limit.' } });
++process.exit(1);
++`, { mode: 0o755 });
++ fs.writeFileSync(path.join(bin, 'claude'), `#!${process.execPath}
++'use strict';
++const out = (o) => console.log(JSON.stringify(o));
++out({ type: 'system', subtype: 'init', session_id: 'cl-cp', model: 'fake-claude' });
++out({ type: 'assistant', message: { content: [{ type: 'text', text: 'finished the note' }] } });
++out({ type: 'result', subtype: 'success', is_error: false, result: 'finished the note', session_id: 'cl-cp' });
++`, { mode: 0o755 });
++
++ const env = {
++ ...process.env, PATH: bin, OPENVIBE_CODES_HOME: home, OPENVIBE_CODES_ALLOW_FILE_REPO: '1',
++ DEEPSEEK_API_KEY: '', OPENVIBE_AI_TOKEN: '', OPENVIBE_APP_ID: '', OPENVIBE_APP_SECRET: '',
++ };
++ const harness = createHarness({ env });
++ const lines = [];
++ let doc = null;
++ try {
++ await runJob({
++ input: JSON.stringify({ kind: 'code', repo: pathToFileURL(repo).href, prompt: 'Add the note', agent: 'codex', task_id: 'tsk_01J8Z3Q4R5S6T7V8W9X0Y1Z2T1' }),
++ harness, env, emit: (line) => lines.push(JSON.parse(line)), writeResult: (d) => { doc = d; },
++ });
++ assert.ok(doc, 'the job wrote a result');
++ // The agent hit its limit, so the job is not ok; the checkpoints of what it did are still there.
++ assert.strictEqual(doc.ok, false);
++ assert.deepStrictEqual(doc.agents, ['codex']);
++ assert.ok(doc.checkpoints, 'the result carries its checkpoints');
++ assert.match(doc.checkpoints.root, /^refs\/codes\/checkpoints\/tsk_01J8Z3Q4R5S6T7V8W9X0Y1Z2T1\/run_/);
++ assert.ok(doc.checkpoints.count >= 2, `count ${doc.checkpoints.count}`);
++ const note = doc.checkpoints.turns.flatMap((t) => t.files).find((f) => f.path === 'notes/turn.txt');
++ assert.ok(note, JSON.stringify(doc.checkpoints.turns));
++ assert.strictEqual(note.authored, true);
++ assert.ok(doc.checkpoints.turn_diff.files.some((f) => f.path === 'notes/turn.txt' && f.authored === true));
++ assert.ok(doc.checkpoints.turns.some((t) => t.files_total > 0));
++ assert.ok(!lines.some((l) => l.t === 'note' && /Checkpoints are off/.test(l.text)), 'checkpoints did not fail');
++ } finally {
++ fs.rmSync(dir, { recursive: true, force: true });
++ }
++ });
++
++ done();
++})().catch((e) => { console.error(e); process.exit(1); });
ddiff --git a/test/engine-runtime.test.js b/test/engine-runtime.test.js
iindex 01e8563..8fb7eab 100644
---- a/test/engine-runtime.test.js
++++ b/test/engine-runtime.test.js
@@@ -31,7 +31,7 @@ const rows = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
await check('a manifest becomes one capability per field, and fits() reads it', () => {
assert.deepStrictEqual(runtime.capabilities(runtime.forHarness('claude-code')), [
-- 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',
++ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-git',
'harness:subagents-native', 'harness:context-handoff-session', 'harness:enforcement-native',
]);
assert.deepStrictEqual(runtime.requirements({ steering: 'resume', subagents: 'native' }), ['harness:steering-resume', 'harness:subagents-native']);
@@@ -55,9 +55,9 @@ const rows = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
for (const id of ['codex:codex-default', 'opencode:opencode-default', 'aider:aider-default', 'deepseek:deepseek-chat']) {
assert.deepStrictEqual(r.candidates.find((c) => c.id === id), { id, included: false, reason: 'lacks harness:subagents-native' });
}
-- const none = route({ task: 'edit', extra: { capabilities: ['harness:checkpointing-git'] }, harnesses, now: Date.now() });
-- assert.strictEqual(none.selected, null, 'no harness declares checkpointing yet');
-- assert.ok(none.candidates.every((c) => c.reason === 'lacks harness:checkpointing-git'));
++ const git = route({ task: 'edit', extra: { capabilities: ['harness:checkpointing-git'] }, harnesses, now: Date.now() });
++ assert.ok(git.selected, 'checkpointing is declared by every catalog harness');
++ assert.ok(git.candidates.every((c) => c.included), 'a job can checkpoint any harness');
});
await check('the tier router skips a ladder rung its manifest cannot meet', () => {
ddiff --git a/test/harness-placement.test.js b/test/harness-placement.test.js
iindex e3c8fa7..67ad0fc 100644
---- a/test/harness-placement.test.js
++++ b/test/harness-placement.test.js
@@@ -41,7 +41,7 @@ function priced(row, freshUsd) {
region: 'global',
trust: 'partner',
capabilities: ['task:edit', 'harness:host-access', 'harness:mcp', 'harness:resume', 'harness:edit', 'harness:tools', 'runtime:code',
-- 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',
++ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-git',
'harness:subagents-none', 'harness:context-handoff-session', 'harness:enforcement-client-boundary'],
capacity: { workers: { harness: 2 } },
health: { status: 'degraded' },
@@@ -55,7 +55,7 @@ function priced(row, freshUsd) {
const offer = toOffer(deepseek, deepseek.agents[0]);
assert.strictEqual(offer.offer_id, 'deepseek:deepseek-chat');
assert.deepStrictEqual(offer.capabilities, ['task:edit', 'task:review', 'harness:host-access', 'harness:resume', 'harness:edit', 'harness:review', 'harness:tools',
-- 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',
++ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-git',
'harness:subagents-none', 'harness:context-handoff-session', 'harness:enforcement-client-boundary']);
assert.strictEqual(offer.trust, 'external');
assert.deepStrictEqual(offer.health, { status: 'up' });
@@@ -78,7 +78,7 @@ function priced(row, freshUsd) {
const browsing = { ...rows[0], task_capabilities: ['edit', 'browse'] };
const offer = toOffer(browsing, browsing.agents[0], { now });
assert.deepStrictEqual(offer.capabilities, ['task:edit', 'task:browse', 'harness:host-access', 'harness:mcp', 'harness:long-autonomy', 'harness:resume', 'harness:edit', 'harness:review', 'harness:tools', 'harness:vision', 'runtime:code',
-- 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',
++ 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-git',
'harness:subagents-native', 'harness:context-handoff-session', 'harness:enforcement-native']);
assert.ok(contracts.validate('platform.resource-offer@1', JSON.parse(JSON.stringify(offer))).valid);
});
ddiff --git a/test/harness-runtime.test.js b/test/harness-runtime.test.js
ddeleted file mode 100644
iindex 01e8563..0000000
---- a/test/harness-runtime.test.js
++++ /dev/null
@@@ -1,77 +0,0 @@
--'use strict';
--/**
-- * The runtime capability manifest (engine/runtime.js): every catalog harness declares what it can do, every offer
-- * carries it as `harness:<field>-<value>` capabilities, and both routers (placement and the tier router) exclude a
-- * harness that does not declare what the task needs — no provider-name conditional in either.
-- */
--const assert = require('assert');
--const fs = require('fs');
--const path = require('path');
--const { createHarnesses } = require('../server/domain/harnesses');
--const { toOffer, route } = require('../server/domain/harness-placement');
--const runtime = require('../engine/runtime');
--const { pick } = require('../engine/router');
--const { check, done } = require('./helpers/boot');
--
--const catalogPath = path.join(__dirname, '..', 'server', 'data', 'harness-offers.json');
--const rows = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
--
--(async () => {
-- await check('every catalog harness declares every field, with a known value', () => {
-- for (const row of rows) {
-- const manifest = runtime.forHarness(row.id);
-- assert.ok(manifest, `${row.id} has no runtime manifest`);
-- assert.deepStrictEqual(Object.keys(manifest).sort(), Object.keys(runtime.FIELDS).sort(), row.id);
-- for (const [field, values] of Object.entries(runtime.FIELDS)) {
-- assert.ok(values.includes(manifest[field]), `${row.id}.${field}: ${manifest[field]} is not one of ${values.join('|')}`);
-- }
-- }
-- assert.deepStrictEqual(Object.keys(runtime.MANIFEST).sort(), rows.map((row) => row.id).sort(), 'no manifest without a catalog row');
-- });
--
-- await check('a manifest becomes one capability per field, and fits() reads it', () => {
-- assert.deepStrictEqual(runtime.capabilities(runtime.forHarness('claude-code')), [
-- 'harness:terminal-status-structured', 'harness:steering-resume', 'harness:checkpointing-none',
-- 'harness:subagents-native', 'harness:context-handoff-session', 'harness:enforcement-native',
-- ]);
-- assert.deepStrictEqual(runtime.requirements({ steering: 'resume', subagents: 'native' }), ['harness:steering-resume', 'harness:subagents-native']);
-- assert.strictEqual(runtime.fits('claude-code', { subagents: 'native' }), true);
-- assert.strictEqual(runtime.fits('codex', { subagents: 'native' }), false);
-- assert.strictEqual(runtime.fits('cx', ['harness:steering-resume']), true, 'the portal runner id names the same harness');
-- assert.strictEqual(runtime.fits('pg', ['harness:steering-resume']), false, 'an unknown runner declares nothing');
-- assert.strictEqual(runtime.fits('pg', []), true, 'nothing required, nothing to exclude');
-- });
--
-- await check('every offer carries the manifest as capabilities the placement router can require', () => {
-- const harnesses = createHarnesses({ catalogPath });
-- for (const harness of harnesses.list()) {
-- const offer = toOffer(harness, harnesses.agents(harness.id)[0]);
-- for (const cap of runtime.capabilities(runtime.forHarness(harness.id))) {
-- assert.ok(offer.capabilities.includes(cap), `${offer.offer_id} lacks ${cap}`);
-- }
-- }
-- const r = route({ task: 'edit', extra: { capabilities: ['harness:subagents-native'] }, harnesses, now: Date.now() });
-- assert.strictEqual(r.selected.split(':')[0], 'claude-code');
-- for (const id of ['codex:codex-default', 'opencode:opencode-default', 'aider:aider-default', 'deepseek:deepseek-chat']) {
-- assert.deepStrictEqual(r.candidates.find((c) => c.id === id), { id, included: false, reason: 'lacks harness:subagents-native' });
-- }
-- const none = route({ task: 'edit', extra: { capabilities: ['harness:checkpointing-git'] }, harnesses, now: Date.now() });
-- assert.strictEqual(none.selected, null, 'no harness declares checkpointing yet');
-- assert.ok(none.candidates.every((c) => c.reason === 'lacks harness:checkpointing-git'));
-- });
--
-- await check('the tier router skips a ladder rung its manifest cannot meet', () => {
-- const ctx = {
-- cfg: { subagents: { mm: { enabled: true, max: 2 }, cm: { enabled: true, max: 2 } } },
-- health: { mm: { state: 'ok' }, cm: { state: 'ok' } },
-- counts: {}, models: {}, runs: [],
-- ladder: { basic: [['mm', null, 'high'], ['cm', null, 'high']] },
-- };
-- assert.strictEqual(pick('Fix the parser', ctx).runner, 'mm', 'no requirement: the first rung stays');
-- assert.strictEqual(pick('Fix the parser', { ...ctx, needs: ['harness:subagents-native'] }).runner, 'cm');
-- assert.strictEqual(pick('Fix the parser', { ...ctx, needs: { steering: 'resume' } }).runner, 'mm');
-- assert.strictEqual(pick('Fix the parser', { ...ctx, needs: ['harness:subagents-native'], ladder: { basic: [['mm', null, 'high']] } }), null);
-- });
--
-- done();
--})().catch((e) => { console.error(e); process.exit(1); });