done-done-axi 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,8 @@
1
1
  <div align="center">
2
2
 
3
- <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="docs/media/wordmark-dark.svg">
5
- <img src="docs/media/wordmark-light.svg" alt="done-done" width="300">
6
- </picture>
3
+ <img src="docs/logo.svg" width="72" height="72" alt="done-done logo">
4
+
5
+ # done-done
7
6
 
8
7
  ### No proof, no "done".
9
8
 
@@ -11,7 +10,25 @@ done-done won't accept "done" without real, fresh proof from your product, captu
11
10
 
12
11
  [![Node 22.18+](https://img.shields.io/badge/node-22.18%2B-3fb950?logo=nodedotjs&logoColor=white)](https://nodejs.org) [![Status: early preview](https://img.shields.io/badge/status-early%20preview-d29922)](#whats-next) [![npm: done-done-axi](https://img.shields.io/npm/v/done-done-axi?logo=npm&logoColor=white&label=npm)](https://www.npmjs.com/package/done-done-axi)
13
12
 
14
- <br>
13
+ </div>
14
+
15
+ ## Install in 3 steps
16
+
17
+ You need Node 22.18 or later and git.
18
+
19
+ ```sh
20
+ npm install -g done-done-axi
21
+ cd your-app && done-done init
22
+ done-done doctor
23
+ ```
24
+
25
+ 1. **Install.** This puts `done-done` (and the long name `done-done-axi`) on your PATH. No `sudo`? Add `--prefix ~/.local` and put `~/.local/bin` on your `PATH`.
26
+ 2. **Set up your repo.** `init` writes a minimal `.done-done.yaml` and asks you, in a terminal, which coding agent and which judge to use. done-done never picks either for you. Outside a terminal, pass `--agent <agent> --model <model> --judge <agent> --judge-model <model>` instead.
27
+ 3. **Check the setup.** `doctor` changes nothing and names each missing piece with the exact command that fixes it.
28
+
29
+ Then prove something: `done-done verify --outcome "<what a user can now do>"`. [Manual CLI](#manual-cli) below walks through journeys, `check` and `receipt`.
30
+
31
+ <div align="center">
15
32
 
16
33
  <picture>
17
34
  <source media="(prefers-color-scheme: dark)" srcset="docs/media/flow-dark.svg">
@@ -59,7 +76,7 @@ npx skills add umeranjum17/done-done-axi --skill done-done -a <agent> -y
59
76
 
60
77
  `<agent>` is `claude-code`, `codex`, `opencode`, `pi` or `gemini-cli`. `-a` and `-y` skip the installer's four prompts and keep it from writing skill folders for agents you do not use.
61
78
 
62
- The skill lands in this project (`.claude/skills/done-done/` for Claude Code, `.agents/skills/done-done/` for Codex, opencode, Pi and Gemini CLI) next to a `skills-lock.json`: commit both so teammates and CI get it too. Or add `-g` to install it once for all your projects (for example `~/.claude/skills/` for Claude Code) and leave the project untouched.
79
+ The skill lands in this project (`.claude/skills/done-done/` for Claude Code, `.agents/skills/done-done/` for Codex, opencode, Pi and Gemini CLI) next to a `skills-lock.json`: commit both so your teammates get it too. Or add `-g` to install it once for all your projects (for example `~/.claude/skills/` for Claude Code) and leave the project untouched.
63
80
 
64
81
  That is the entire setup - no npm install needed.
65
82
  The skill tells your agent to use `done-done` if it is on PATH and otherwise run the npm package through `npx`, so the CLI comes along on demand:
@@ -68,7 +85,7 @@ The skill tells your agent to use `done-done` if it is on PATH and otherwise run
68
85
  npx -y done-done-axi --help
69
86
  ```
70
87
 
71
- The skill stays a short stub and sends the agent to `--help` and the bare command for current instructions, so an installed copy cannot go stale against a newer CLI.
88
+ The skill is a short stub and sends the agent to `--help` and the bare command for current instructions, so an installed copy cannot go stale against a newer CLI.
72
89
 
73
90
  Then, in agents that expose skills as slash commands (Claude Code, for example), invoke it directly:
74
91
 
@@ -101,7 +118,7 @@ cd your-app
101
118
  done-done init
102
119
  ```
103
120
 
104
- `init` writes a minimal `.done-done.yaml` (only if you don't already have one) that names your app, and a commented global `~/.done-done/config.yaml` (only if there is none). done-done never picks the coding agent it starts: `init` asks you in a terminal, or takes `--agent <agent> --model <model>`. It never picks the judge either, the agent of its own that `check`, `receipt`, `verify` and `design` always start to look at each capture: `init` asks in a terminal, or takes `--judge <agent> --judge-model <model>`. Any agent and model you choose runs, your coding agent's own too (the judge is a fresh session of its own). The config keys are in the [reference](docs/reference.md#config). A plain `init` never overwrites an existing config. It writes no verify skill: write `.agents/skills/verify-<app>/SKILL.md` by hand, with a launch that never touches real accounts or your own state. Evidence lives outside the repo, in `~/.done-done/evidence/<app>`, so it outlives the checkout that captured it. For an `answerer:pi` judge with no explicit model, done-done uses `openai-codex/gpt-6-luna`; `judge.model` or `agent_config.pi.model` overrides it. Then describe how to capture proof — swap the command for whatever drives your real app and saves screenshots, recordings or logs:
121
+ `init` writes a minimal `.done-done.yaml` (only if you don't already have one) that names your app, and a commented global `~/.done-done/config.yaml` (only if there is none). done-done never picks the coding agent it starts: `init` asks you in a terminal, or takes `--agent <agent> --model <model>`. It never picks the judge either, the agent of its own that `check`, `receipt`, `verify` and `design` always start to look at each capture: `init` asks in a terminal, or takes `--judge <agent> --judge-model <model>`. Any agent and model you choose runs, your coding agent's own too (the judge is a fresh session of its own). The config keys are in the [reference](docs/reference.md#config). A plain `init` never overwrites an existing config. When your coding agent is set, `init` also has it write a verify skill, `.agents/skills/verify-<app>/SKILL.md`, from the repo's own files (never overwriting one; with no agent set, no skill is written). Check its launch never touches real accounts or your own state. Evidence lives outside the repo, in `~/.done-done/evidence/<app>`, so it outlives the checkout that captured it. For an `answerer:pi` judge with no explicit model, done-done uses `openai-codex/gpt-6-luna`; `judge.model` or `agent_config.pi.model` overrides it. Then describe how to capture proof — swap the command for whatever drives your real app and saves screenshots, recordings or logs:
105
122
 
106
123
  ```yaml
107
124
  journeys:
@@ -158,18 +175,29 @@ Run `done-done` with no arguments to see the current config and the next step.
158
175
  - ⏱️ **Fresh**: Proof from an older commit fails. Commits that only touch docs can be set to keep it fresh.
159
176
  - ♻️ **Not reused**: Proof made only of files already used for a different claim fails, and so does citing the same file twice. A claim is named by `--claim-id <id>`, which defaults to your branch, so rewording it keeps its proof. Several claims on one branch each need their own `--claim-id`.
160
177
  - 🧾 **Honest claims**: Reads a pull request's `## Checks` block — from a file, a URL, or stdin — and makes sure every line is filled in.
161
- - 📌 **Verifiable later**: `receipt` records a passing check against the exact commit and the sha256 of every file it cited; `receipt --verify` re-checks it from any checkout of that commit on the same machine (a merge gate or a fresh clone), or in CI once the evidence folder is copied there ([how](docs/reference.md#where-proof-lives)).
178
+ - 📌 **Verifiable later**: `receipt` records a passing check against the exact commit and the sha256 of every file it cited; `receipt --verify` re-checks it from any checkout of that commit on the same machine (a merge gate or a fresh clone) ([how](docs/reference.md#where-proof-lives)).
162
179
  - 👀 **Shows the outcome** (always): `check`, `receipt` and `verify` start done-done's own judge, a fresh, tools-off agent on your own plan or key (Claude Code, opencode, pi or Gemini CLI, or your own OpenAI API key), to say whether each capture is the right screen for the claim (and each text capture, such as a log, JSON or CLI output, the right output), and a wrong one fails. There is no way to turn it off: done-done gives no result without it. The checks above never pass on their own, and a check that cites only files the judge cannot read (a video) stops before the judge, with exit 2. You choose the judge route (`done-done init --judge <agent> --judge-model <model>`, or pick it from the list done-done shows in a terminal; `done-done check --judge <route>` overrides it for one call); for a Pi judge with no explicit model, done-done defaults to `openai-codex/gpt-6-luna` unless `agent_config.pi.model` is set. Any agent and model you choose runs, your coding agent's own too. A judge that fails or times out stops the command with its cause (exit 2; a `verify` run stops with exit 2 and its cause in the run record), and no receipt is written.
163
180
  - 🎨 **Looks right**: `done-done design <screens...> [--mock <screen>=<mock>]` has the same judge look at each captured screen, alone or next to its approved mock, and fails it on stretched or squashed images, clipped or squashed text, overlap, elements off-screen, blank or black strips and broken layout, naming each defect and where it is ([how](docs/reference.md#design-check)).
164
181
  - 📐 **Fits the screen**: `done-done look <url>` opens a web page in headless Chrome at a 390x844 phone viewport, captures it, and fails the journey that calls it on squashed text, clipped text, elements cut off by an edge or a page wider than the screen, naming each element, with no model ([how](docs/reference.md#look-check)).
165
182
  - 🧮 **Honest, cheap claim gate**: `done-done lint <PR URL | report file> <evidence folder>` is a no-model gate on a worker's `## Checks` block, on whenever you call it. It prints `PASS` or `FAIL: <reason>` and exits 1 on a fail: the four lines present, proof naming a command or path, a real repro on a fix PR, evidence files that exist, are non-empty, are newer than the evidence folder and differ from each other, at least one never used by another PR (hashes kept in `~/.done-done/lint.hashes`, every run logged to `lint.log` there), and under 1,000 added non-generated lines ([how](docs/reference.md#lint-check)). `evidence: not proven: <cause>` passes.
183
+ - 🚦 **CI gate, no model**: `done-done gate [<head>]` passes when a receipt committed under `.done-done-receipts/` covers the head, with no judge, config, key or evidence files. CI never runs `check`, `run` or `verify` and never gets a model key; proof runs on your desktop ([how](docs/reference.md#ci-gate)).
166
184
  - 🧩 **Page is clean** (with a `dom` block): each captured screen also needs its page-check record. A page that overflows the viewport, logs a console error, has a failed request or shows squashed, clipped or cut-off UI fails, unless an allow list names it ([how](docs/reference.md#dom-assertions)).
167
- - 🔒 **Safe in CI**: `--trusted-ref origin/main` reads the rules from your main branch, so a pull request can't loosen them.
168
185
 
169
186
  Every answer is an exit code: **0** pass, **1** fail, **2** usage or config error (including no agent or judge set, a check that fails before its judge, a capture the judge cannot be sent, or a judge that did not answer: never a result), **3** can't tell either way, **4** a `verify`, `maintain` or `rerun` run is still running, or a `verify` run waits at its gate.
170
187
 
188
+ ## Alone, and with the other tools
189
+
190
+ done-done works on its own: the CLI and the skill need nothing else. It is one of three tools that each do one job and connect through small written contracts ([Vision](VISION.md)):
191
+
192
+ - [`sign-off-axi`](https://www.npmjs.com/package/sign-off-axi): the board where agents ask you questions. `done-done signoff` writes a run's gate (when the judge was unsure) or its evidence in sign-off's message format and prints the post command; it never posts. Without sign-off you answer at the terminal with `done-done respond`.
193
+ - [`learn-from-mistakes-axi`](https://www.npmjs.com/package/learn-from-mistakes-axi): the retro tool that finds mistakes your agents keep repeating, with evidence. Neither tool needs the other.
194
+ - [`first-draft-axi`](https://www.npmjs.com/package/first-draft-axi): turns a prompt into design mocks. Pass a mock to `done-done design <screen> --mock <screen>=<mock>` to judge a captured screen next to it.
195
+
171
196
  ## Use with Firstmate or another agent fleet
172
197
 
198
+ done-done works the same with or without Firstmate: nothing below is needed to use it, and the skill and CLI behave identically inside or outside a Firstmate home.
199
+
200
+
173
201
  [Firstmate](https://github.com/kunchenguid/firstmate) hands tasks to agent workers and merges their work. done-done ships what those workers need to prove each task, and what the merge needs to check that proof, in [`integrations/firstmate/`](integrations/firstmate/).
174
202
 
175
203
  Install the skill once per machine for your workers' agent (the same `-a` values), install the CLI from npm, and copy two files from it into your Firstmate home:
@@ -195,7 +223,7 @@ Another fleet works the same way: add the include text to whatever your workers
195
223
 
196
224
  ## What's next
197
225
 
198
- So far: `init`, `doctor`, `run` (including `--open` and `--close`), `check`, `receipt`, `verify` (by journey or by your coding agent, with a gate you answer with `respond`), `maintain`, the capture judge (always on), the `design` visual check, the `look` step, the run tools (`status`, `attach`, `logs`, `rerun`, `abort`, `signoff`, `history`) and the opt-in session hooks for Claude Code, Codex, opencode, Gemini CLI and pi. Coming next: help writing the proof steps for your app.
226
+ So far: `init`, `doctor`, `run` (including `--open` and `--close`), `check`, `receipt`, `lint` (a no-model gate on a Checks block), `gate` (a no-model CI check of a committed receipt), `verify` (by journey or by your coding agent, with a gate you answer with `respond`), `maintain`, the capture judge (always on), the `design` visual check, the `look` step, the run tools (`status`, `attach`, `logs`, `rerun`, `abort`, `signoff`, `history`) and the opt-in session hooks for Claude Code, Codex, opencode, Gemini CLI and pi. Coming next: help writing the proof steps for your app.
199
227
 
200
228
  ## Develop
201
229
 
@@ -206,7 +234,7 @@ npm install
206
234
  npm install -g --prefix ~/.local . # optional: links this checkout as done-done on your PATH
207
235
  npm test # builds, then runs the end-to-end tests against throwaway git repos
208
236
  npm run build:skill # regenerate skills/done-done/SKILL.md from src/skill.ts
209
- npm run build:skill -- --check # fail if the committed skill is out of date (CI runs this)
237
+ npm run build:skill -- --check # fail if the committed skill is out of date
210
238
  ```
211
239
 
212
240
  To try an unreleased commit without cloning, run it straight from GitHub (npm 12 needs `--allow-git=all`):
@@ -217,4 +245,4 @@ npx -y --allow-git=all github:umeranjum17/done-done-axi --help
217
245
 
218
246
  `dist/` is committed, so installing from GitHub needs no build step. `npm test` rebuilds it and fails while the rebuilt `dist/` differs from the commit or has files git does not track, so `git add` the new `dist/` files with your change.
219
247
 
220
- The terminal pictures (hero, run, pass, reasons, config) are real output from the current build, with a stub judge (no model); redraw them with `npx tsc && node docs/media/make.mjs`. The wordmark and the flow picture are hand-made SVGs.
248
+ The terminal pictures (hero, run, pass, reasons, config) are real output from the current build, with a stub judge (no model); redraw them with `npx tsc && node docs/media/make.mjs`. The logo (`docs/logo.svg`) and the flow picture are hand-made SVGs.
package/dist/cli.js CHANGED
@@ -93,7 +93,7 @@ const FLAGS = {
93
93
  '--close <run-id>': 'seal an open run and record the files in its folder',
94
94
  '--outcome "<claim>"': 'the outcome the evidence files prove; the files follow as arguments',
95
95
  '--from <file | - | PR URL>': 'read the claim from the newest "## Checks" block of a file, stdin or a PR body',
96
- '--trusted-ref <ref>': 'read .done-done.yaml from this git ref instead of the working tree (use in CI)',
96
+ '--trusted-ref <ref>': 'read .done-done.yaml from this git ref instead of the working tree (for scripts)',
97
97
  '--judge <route>': 'check with this judge for this call instead of judge.route (same form: claude-code, answerer:opencode, answerer:pi, answerer:gemini or openai:key); its model is that agent\'s default, or judge.model when the route is the configured one',
98
98
  '--citation-base <dir>': 'resolve evidence citations against this directory (default: cwd); absolute citations stay unchanged; does not change repo, config or run storage',
99
99
  '--claim-id <id>': 'the claim the evidence proves; evidence already used for another claim id is reuse (default: the current branch, or the PR branch with --from <PR URL>; required on a detached HEAD)',
@@ -101,7 +101,7 @@ const FLAGS = {
101
101
  '--outcome "<what a user can now do>"': 'the outcome the run must prove',
102
102
  '--wait': 'wait until the run ends (the default)',
103
103
  '--no-wait': 'start the run and return at once (exit 4); read it later with done-done status',
104
- '--foreground': 'run every step in this process instead of a detached runner (use in CI)',
104
+ '--foreground': 'run every step in this process instead of a detached runner (for scripts)',
105
105
  '--run <run-id>': 'the run to show (default: the newest run of this checkout)',
106
106
  '--probe': 'ask the configured agent one JSON-schema question and require a valid answer (one call at the agent\'s model: its cheap default where it has one, else its own; set agent_config.<agent>.model to choose)',
107
107
  '--mock <screen>=<mock>': 'judge this screen next to its approved mock (repeat per screen; the screen is judged even when not listed)',
@@ -174,7 +174,7 @@ const COMMANDS = {
174
174
  usage: [
175
175
  ['done-done verify [<journey>] --outcome "<what a user can now do>" [--claim-id <id>]', 'drive the journey in a run that outlives this command, check its evidence, judge each capture and record a receipt'],
176
176
  ['done-done verify --outcome "<claim>" [--feature <name>]', 'with no journey, the author agent drives a verify skill feature and the judge the user set judges its captures'],
177
- ['done-done verify [<journey>] --outcome "<claim>" --no-wait | --wait | --foreground', 'return at once, wait until the run ends or waits at its gate (default), or run in this process (CI)'],
177
+ ['done-done verify [<journey>] --outcome "<claim>" --no-wait | --wait | --foreground', 'return at once, wait until the run ends or waits at its gate (default), or run in this process'],
178
178
  ],
179
179
  flags: ['--outcome "<what a user can now do>"', '--feature <name>', '--claim-id <id>', '--wait', '--no-wait', '--foreground'],
180
180
  more: [RUNS_TEXT],
@@ -198,6 +198,15 @@ const COMMANDS = {
198
198
  overflow, squashed text, text clipped by its container and elements cut off by the left, right or top edge, writes the
199
199
  screenshot and its .dom.json record, and exits 1 naming each element, kind and text, 0 when there is none. A hit always
200
200
  fails: never wrap it in || true. Needs Chrome or Chromium (DONE_DONE_CHROME names the binary)`],
201
+ },
202
+ gate: {
203
+ usage: [['done-done gate [<head>] [--dir <folder>] [--pushed <time>]', 'no-model check for a hosted runner: PASS or FAIL: <reason> for a receipt committed under .done-done-receipts/']],
204
+ flags: ['--dir <folder>', '--pushed <time>'],
205
+ more: [`gate: reads git objects only, so it needs no config, judge, model key or evidence files, and a hosted runner never runs check, run or verify.
206
+ Copy the receipt that receipt wrote (<evidence root>/.done-done/receipts/<head>.json) into .done-done-receipts/ and commit it in a follow-up commit.
207
+ PASS when a receipt there is a well-formed judged pass, its head is <head> (default HEAD) or an ancestor of it, every file changed since that head is
208
+ inside the folder, and its time is not after the push (--pushed, default the commit time of <head>). It does not re-hash evidence: that is receipt --verify
209
+ on the machine that holds it. Needs the history back to the receipt head (fetch-depth: 0). Exit 0 PASS, 1 FAIL, 2 usage error`],
201
210
  },
202
211
  lint: {
203
212
  usage: [['done-done lint <PR URL | report file> <evidence folder>', 'merge gate on a worker\'s claim: prints PASS or FAIL: <reason>; the rules need no judge, model or config']],
@@ -572,7 +581,7 @@ function claimFromChecks(body, title, repo, cfg, out) {
572
581
  out.push({ level: 'fail', why: 'evidence names no files' });
573
582
  return { outcome: f.outcome, files, title };
574
583
  }
575
- // A PR body also names its head branch, which is the claim a CI checkout (a detached HEAD) is checking.
584
+ // A PR body also names its head branch, which is the claim a detached-HEAD checkout is checking.
576
585
  function readSource(from) {
577
586
  if (from === '-')
578
587
  return { body: readFileSync(0, 'utf8') };
@@ -823,7 +832,7 @@ async function cmdCheck(argv) {
823
832
  }
824
833
  const receiptsDir = (cfg) => join(cfg.evidence.root, '.done-done', 'receipts');
825
834
  // A receipt names a cited file relative to the repo, or to an evidence root outside it under this prefix,
826
- // so any checkout of the repo (or a CI job that restored the evidence root) finds the same file.
835
+ // so any checkout of the repo finds the same file.
827
836
  const IN_EVIDENCE = '<evidence root>/';
828
837
  function receiptPath(repo, cfg, p) {
829
838
  const inRepo = under(repo, p);
@@ -839,6 +848,17 @@ function writeReceipt(cfg, rec) {
839
848
  renameSync(`${path}.tmp`, path);
840
849
  return path;
841
850
  }
851
+ // Why a parsed receipt is not a well-formed passing one (a judge block that names its judge and passes only captures the receipt cites), or null.
852
+ function receiptFault(rec, short) {
853
+ if (!rec || typeof rec !== 'object' || rec.schema !== 2 || rec.result !== 'pass' || !Array.isArray(rec.files) || !rec.files.length)
854
+ return `receipt for ${short} is malformed`;
855
+ if (!rec.files.every((f) => f && typeof f === 'object' && typeof f.path === 'string' && typeof f.sha256 === 'string'))
856
+ return `receipt for ${short} is malformed`;
857
+ const j = rec.judge;
858
+ const badJudge = !j || typeof j !== 'object' || typeof j.route !== 'string' || typeof j.model !== 'string' || !['agent', 'model', 'session-only'].includes(j.independence) || !Array.isArray(j.verdicts) || !j.verdicts.length
859
+ || !j.verdicts.every((v) => v && v.verdict === 'pass' && rec.files.some((f) => f.path === v.path && f.sha256 === v.sha256));
860
+ return badJudge ? `receipt for ${short} has a malformed judge block` : null;
861
+ }
842
862
  // Verify a recorded receipt for `headArg`, which may be a full sha or a ref (HEAD, a branch or a tag).
843
863
  // Read-only, so a missing or changed record is a failure, not a usage error.
844
864
  function verifyReceipt(repo, cfg, headArg) {
@@ -861,16 +881,10 @@ function verifyReceipt(repo, cfg, headArg) {
861
881
  catch (e) {
862
882
  return fail(`receipt for ${short} is malformed (${e.message})`);
863
883
  }
864
- if (!rec || typeof rec !== 'object' || rec.schema !== 2 || rec.result !== 'pass' || !Array.isArray(rec.files) || !rec.files.length)
865
- return fail(`receipt for ${short} is malformed`);
866
- if (!rec.files.every((f) => f && typeof f === 'object' && typeof f.path === 'string' && typeof f.sha256 === 'string'))
867
- return fail(`receipt for ${short} is malformed`);
868
- // The judge block must name its judge, and pass only captures this receipt cites.
884
+ const fault = receiptFault(rec, short);
885
+ if (fault)
886
+ return fail(fault);
869
887
  const j = rec.judge;
870
- const badJudge = !j || typeof j !== 'object' || typeof j.route !== 'string' || typeof j.model !== 'string' || !['agent', 'model', 'session-only'].includes(j.independence) || !Array.isArray(j.verdicts) || !j.verdicts.length
871
- || !j.verdicts.every((v) => v && v.verdict === 'pass' && rec.files.some((f) => f.path === v.path && f.sha256 === v.sha256));
872
- if (badJudge)
873
- return fail(`receipt for ${short} has a malformed judge block`);
874
888
  if (rec.head !== commit)
875
889
  return fail(`receipt records head ${String(rec.head).slice(0, 12)}, not ${short}`);
876
890
  const now = head(repo);
@@ -935,7 +949,7 @@ function recordReceipt(repo, cfg, r) {
935
949
  const rec = { schema: 2, repo: cfg.app, head: r.head.commit, tree: r.head.tree, claim: r.claim ?? '', result: 'pass', at: new Date().toISOString(), files: r.cited, judge: r.judgeBlock };
936
950
  const path = writeReceipt(cfg, rec);
937
951
  const result = { command: 'receipt', result: 'pass', head: r.head.commit, tree: r.head.tree, claim: rec.claim, files: rec.files.length, receipt: shown(repo, path) };
938
- return { code: PASS, result: { ...result, ...(r.judge && { judge: r.judge }), help: [`Verify this receipt (CI or merge): done-done receipt --verify ${r.head.commit}`] } };
952
+ return { code: PASS, result: { ...result, ...(r.judge && { judge: r.judge }), help: [`Verify this receipt (merge gate): done-done receipt --verify ${r.head.commit}`] } };
939
953
  }
940
954
  // Why evidence from `from` no longer proves `to`, or '' if it still does.
941
955
  function staleness(repo, cfg, from, to) {
@@ -961,10 +975,10 @@ function checkHelp(code, findings, files, { cfg, repo, claim, source, citationBa
961
975
  if (judgeLevel && code === INCONCLUSIVE)
962
976
  return ['Look at the capture yourself, or capture a clearer screen of the outcome and re-run this check'];
963
977
  if (code === PASS) {
964
- // The CI gate needs a PR on a remote; without one, a receipt records the pass locally.
978
+ // The merge gate needs a PR on a remote; without one, a receipt records the pass locally.
965
979
  const citationArgs = citationBase === undefined ? '' : ` --citation-base ${arg(citationBase)}`;
966
980
  if (spawnSync('git', ['remote', 'get-url', 'origin'], { cwd: repo }).status === 0)
967
- return [`Gate CI on this proof: done-done check --from <PR URL> --trusted-ref origin/main${citationArgs}`];
981
+ return [`Gate the merge on this proof: done-done check --from <PR URL> --trusted-ref origin/main${citationArgs}`];
968
982
  const claimArgs = source ? ['--from', source] : ['--outcome', claim ?? '', ...files];
969
983
  return [`Record this pass for HEAD: done-done receipt ${claimArgs.map(arg).join(' ')}${citationArgs}`];
970
984
  }
@@ -1892,7 +1906,7 @@ function showRun(dir, cfg, command, gone = false) {
1892
1906
  : /^locate: no verify skill/.test(run.reason ?? '') ? ['Create the verify skill: done-done init']
1893
1907
  : /^locate:/.test(run.reason ?? '') ? [`Make ${cfg.skill} a real folder inside the repository (not a link), commit, then run again: ${again}`]
1894
1908
  : [`Read the step logs in ${shown(run.repo, join(dir, 'logs'))}`, `Fix the cause, then run again: ${again}`])
1895
- : code === PASS ? [`Verify this receipt (CI or merge): done-done receipt --verify ${rec.commit}`]
1909
+ : code === PASS ? [`Verify this receipt (merge gate): done-done receipt --verify ${rec.commit}`]
1896
1910
  : status === 'cancelled' ? [...(run.kind === 'maintain' ? [] : [`Start again from the failed step: done-done rerun --run ${run.run_id}`]), `List past runs: done-done history`]
1897
1911
  : [`Read the step logs in ${shown(run.repo, join(dir, 'logs'))}`, ...(run.kind === 'maintain' ? [] : [`Fix the cause, commit, then run again from the failed step: done-done rerun --run ${run.run_id}`]), `Or start a new run: ${again}`];
1898
1912
  emit(result);
@@ -3478,6 +3492,69 @@ async function cmdLook(argv) {
3478
3492
  });
3479
3493
  return hits.length ? FAIL : PASS;
3480
3494
  }
3495
+ // The folder of a branch where a receipt copied from the evidence root is committed, for `gate`.
3496
+ const GATE_DIR = '.done-done-receipts';
3497
+ // The reason no committed receipt vouches for `tip`, or null when one does. Reads git objects only: no config, no judge, no evidence files.
3498
+ function gateFault(repo, tip, dir, pushed) {
3499
+ const short = tip.slice(0, 12);
3500
+ const names = git(repo, '-c', 'core.quotepath=off', 'ls-tree', '-r', '--name-only', tip, '--', dir).split('\n').filter((n) => n.endsWith('.json'));
3501
+ if (!names.length)
3502
+ return `no receipt in ${dir}/ at ${short}`;
3503
+ const reasons = [];
3504
+ for (const name of names) {
3505
+ const why = (() => {
3506
+ let rec;
3507
+ try {
3508
+ rec = JSON.parse(git(repo, 'show', `${tip}:${name}`));
3509
+ }
3510
+ catch (e) {
3511
+ return `${name} is malformed (${e.message})`;
3512
+ }
3513
+ const fault = receiptFault(rec, basename(name));
3514
+ if (fault)
3515
+ return fault;
3516
+ if (name !== `${dir}/${rec.head}.json`)
3517
+ return `${name} does not name its own head ${String(rec.head).slice(0, 12)}`;
3518
+ try {
3519
+ git(repo, 'merge-base', '--is-ancestor', rec.head, tip);
3520
+ }
3521
+ catch {
3522
+ return `receipt head ${rec.head.slice(0, 12)} is not ${short} or an ancestor of it (a shallow checkout needs full history)`;
3523
+ }
3524
+ const outside = git(repo, '-c', 'core.quotepath=off', 'diff', '--name-only', rec.head, tip).split('\n').filter((n) => n && !n.startsWith(`${dir}/`));
3525
+ if (outside.length)
3526
+ return `${named(outside)} changed after receipt head ${rec.head.slice(0, 12)}, so its proof no longer covers ${short}`;
3527
+ const at = Date.parse(rec.at);
3528
+ if (!(Math.floor(at / 1000) <= Math.floor(pushed / 1000)))
3529
+ return `receipt time ${rec.at} is not before the push (${new Date(pushed).toISOString()})`;
3530
+ return null;
3531
+ })();
3532
+ if (!why)
3533
+ return null;
3534
+ reasons.push(why);
3535
+ }
3536
+ return reasons.join('; ');
3537
+ }
3538
+ // `done-done gate`: the no-model check a hosted runner can run. A receipt committed under GATE_DIR vouches for the head.
3539
+ function cmdGate(argv) {
3540
+ const { values, positionals } = parseArgs({ args: argv, options: { dir: { type: 'string' }, pushed: { type: 'string' } }, allowPositionals: true });
3541
+ if (positionals.length > 1)
3542
+ throw new UsageError('gate takes at most one head', 'Re-run as: done-done gate [<head>] [--dir <folder>] [--pushed <time>]');
3543
+ const repo = repoRoot();
3544
+ let tip;
3545
+ try {
3546
+ tip = git(repo, 'rev-parse', '--verify', `${positionals[0] ?? 'HEAD'}^{commit}`);
3547
+ }
3548
+ catch {
3549
+ throw new UsageError(`gate: ${positionals[0]} is not a commit in this checkout`, 'Pass the pull request head sha, or run it in a checkout of that head');
3550
+ }
3551
+ const pushed = values.pushed === undefined ? Number(git(repo, 'log', '-1', '--format=%ct', tip)) * 1000 : Date.parse(values.pushed);
3552
+ if (Number.isNaN(pushed))
3553
+ throw new UsageError(`gate: --pushed ${values.pushed} is not a time`, 'Pass an ISO time such as 2026-10-20T09:05:56Z');
3554
+ const why = gateFault(repo, tip, (values.dir ?? GATE_DIR).replace(/\/+$/, ''), pushed);
3555
+ console.log(why === null ? 'PASS' : `FAIL: ${why}`);
3556
+ return why === null ? PASS : FAIL;
3557
+ }
3481
3558
  function cmdLint(argv) {
3482
3559
  const { positionals } = parseArgs({ args: argv, allowPositionals: true });
3483
3560
  if (positionals.length !== 2)
@@ -3611,6 +3688,8 @@ async function main(argv) {
3611
3688
  return await cmdLook(rest);
3612
3689
  if (cmd === 'lint')
3613
3690
  return cmdLint(rest);
3691
+ if (cmd === 'gate')
3692
+ return cmdGate(rest);
3614
3693
  if (cmd === 'status')
3615
3694
  return cmdStatus(rest);
3616
3695
  if (cmd === 'attach')
package/dist/look.js CHANGED
@@ -7,7 +7,7 @@ import { domRecord, LOOK, SQUASHED_TEXT } from './dom.js';
7
7
  import { findBin } from './agents.js';
8
8
  // done-done look: opens one page in headless Chrome at a phone viewport over the DevTools protocol (Node's own
9
9
  // WebSocket, no browser library), writes the screenshot and its page-check record next to it, and returns the record.
10
- // Chrome runs without its sandbox so it starts in containers and CI: point look at your own app, not untrusted sites.
10
+ // Chrome runs without its sandbox so it starts in containers: point look at your own app, not untrusted sites.
11
11
  const BROWSERS = ['google-chrome', 'google-chrome-stable', 'chromium', 'chromium-browser', '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'];
12
12
  const WAIT = 30_000;
13
13
  export async function look(url, capture, width, height) {
package/docs/logo.svg ADDED
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="72" height="72" viewBox="0 0 24 24" role="img" aria-label="done-done logo"><style>.t{fill:#4E5BC9}.c{fill:none;stroke:#FFFFFF;stroke-width:2.4;stroke-linecap:round;stroke-linejoin:round}@media (prefers-color-scheme:dark){.t{fill:#6C78E0}}</style><rect class="t" width="24" height="24" rx="6"/><path class="c" d="M3.9 12.6l2.6 2.6 4.1-5"/><path class="c" d="M10.4 13.2l2.4 2.4 6.7-8"/></svg>
@@ -119,7 +119,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
119
119
  <g class="l4"><text xml:space="preserve" x="22" y="168" textLength="172.8" lengthAdjust="spacing"><tspan class="d">source: </tspan>pr-body.md</text></g>
120
120
  <g class="l5"><text xml:space="preserve" x="22" y="192" textLength="76.8" lengthAdjust="spacing"><tspan class="d">files: </tspan>1</text></g>
121
121
  <g class="l6"><text xml:space="preserve" x="22" y="216" textLength="393.6" lengthAdjust="spacing"><tspan class="d">evidence[1]: </tspan>~/.done-done/evidence/notes/</text></g>
122
- <g class="l7"><text xml:space="preserve" x="22" y="240" textLength="441.6" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png</text></g>
122
+ <g class="l7"><text xml:space="preserve" x="22" y="240" textLength="441.6" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png</text></g>
123
123
  <g class="l8"><text xml:space="preserve" x="22" y="264" textLength="57.6" lengthAdjust="spacing">judge:</text></g>
124
124
  <g class="l9"><text xml:space="preserve" x="22" y="288" textLength="249.6" lengthAdjust="spacing"> route: "answerer:gemini"</text></g>
125
125
  <g class="l10"><text xml:space="preserve" x="22" y="312" textLength="182.4" lengthAdjust="spacing"> model: demo-judge</text></g>
@@ -127,7 +127,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
127
127
  <g class="l12"><text xml:space="preserve" x="22" y="360" textLength="211.2" lengthAdjust="spacing"> author: no agent set</text></g>
128
128
  <g class="l13"><text xml:space="preserve" x="22" y="384" textLength="432.0" lengthAdjust="spacing"> judged[1]{path,verdict,reason,seen,cached}:</text></g>
129
129
  <g class="l14"><text xml:space="preserve" x="22" y="408" textLength="307.2" lengthAdjust="spacing"> ~/.done-done/evidence/notes/</text></g>
130
- <g class="l15"><text xml:space="preserve" x="22" y="432" textLength="355.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/</text></g>
130
+ <g class="l15"><text xml:space="preserve" x="22" y="432" textLength="355.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/</text></g>
131
131
  <g class="l16"><text xml:space="preserve" x="22" y="456" textLength="441.6" lengthAdjust="spacing"> after.png,pass,The screen shows the synced</text></g>
132
132
  <g class="l17"><text xml:space="preserve" x="22" y="480" textLength="355.2" lengthAdjust="spacing"> note.,The screen shows the synced</text></g>
133
133
  <g class="l18"><text xml:space="preserve" x="22" y="504" textLength="144.0" lengthAdjust="spacing"> note.,false</text></g>
@@ -141,10 +141,10 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
141
141
  <g class="l26"><text xml:space="preserve" x="22" y="696" textLength="134.4" lengthAdjust="spacing"><tspan class="d">command: </tspan>check</text></g>
142
142
  <g class="l27"><text xml:space="preserve" x="22" y="720" textLength="460.8" lengthAdjust="spacing"><tspan class="f b">error: </tspan>"check: the judge was not asked, so there</text></g>
143
143
  <g class="l28"><text xml:space="preserve" x="22" y="744" textLength="451.2" lengthAdjust="spacing"> is no verdict: ~/.done-done/evidence/notes/</text></g>
144
- <g class="l29"><text xml:space="preserve" x="22" y="768" textLength="451.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png:</text></g>
145
- <g class="l30"><text xml:space="preserve" x="22" y="792" textLength="460.8" lengthAdjust="spacing"> evidence is for commit 396dc99b6db7, HEAD is</text></g>
146
- <g class="l31"><text xml:space="preserve" x="22" y="816" textLength="422.4" lengthAdjust="spacing"> a890428671ea (changed: src/sync.js) (run</text></g>
147
- <g class="l32"><text xml:space="preserve" x="22" y="840" textLength="364.8" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550)"</text></g>
144
+ <g class="l29"><text xml:space="preserve" x="22" y="768" textLength="451.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png:</text></g>
145
+ <g class="l30"><text xml:space="preserve" x="22" y="792" textLength="460.8" lengthAdjust="spacing"> evidence is for commit fedec802045e, HEAD is</text></g>
146
+ <g class="l31"><text xml:space="preserve" x="22" y="816" textLength="422.4" lengthAdjust="spacing"> cce55aa4e026 (changed: src/sync.js) (run</text></g>
147
+ <g class="l32"><text xml:space="preserve" x="22" y="840" textLength="364.8" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2)"</text></g>
148
148
  <g class="l33"><text xml:space="preserve" x="22" y="864" textLength="432.0" lengthAdjust="spacing"><tspan class="d">help[1]: </tspan>"Commit your change, re-capture with</text></g>
149
149
  <g class="l34"><text xml:space="preserve" x="22" y="888" textLength="403.2" lengthAdjust="spacing"> done-done run sync, then check the new</text></g>
150
150
  <g class="l35"><text xml:space="preserve" x="22" y="912" textLength="96.0" lengthAdjust="spacing"> files"</text></g>
@@ -152,18 +152,18 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
152
152
  <g class="l37"><text xml:space="preserve" x="22" y="72" textLength="268.8" lengthAdjust="spacing"><tspan class="c">~/notes $ </tspan><tspan class="b">done-done run sync</tspan></text><g class="k37"><rect x="118" y="55" width="192.8" height="24" fill="#161b22"/><rect class="h37" x="118" y="56" width="9.6" height="20" fill="#e6edf3" opacity=".7"/></g></g>
153
153
  <g class="l38"><text xml:space="preserve" x="22" y="96" textLength="115.2" lengthAdjust="spacing"><tspan class="d">command: </tspan>run</text></g>
154
154
  <g class="l39"><text xml:space="preserve" x="22" y="120" textLength="153.6" lengthAdjust="spacing"><tspan class="d">result: </tspan><tspan class="p b">recorded</tspan></text></g>
155
- <g class="l40"><text xml:space="preserve" x="22" y="144" textLength="384.0" lengthAdjust="spacing"><tspan class="d">run_id: </tspan>20261018T081521Z-396dc99b-969550</text></g>
156
- <g class="l41"><text xml:space="preserve" x="22" y="168" textLength="460.8" lengthAdjust="spacing"><tspan class="d">commit: </tspan>396dc99b6db71f41bac00a43ac75439e6fb1e91c</text></g>
155
+ <g class="l40"><text xml:space="preserve" x="22" y="144" textLength="384.0" lengthAdjust="spacing"><tspan class="d">run_id: </tspan>20261020T065410Z-fedec802-12efe2</text></g>
156
+ <g class="l41"><text xml:space="preserve" x="22" y="168" textLength="460.8" lengthAdjust="spacing"><tspan class="d">commit: </tspan>fedec802045ebc823249c819dcfc195647ebad75</text></g>
157
157
  <g class="l42"><text xml:space="preserve" x="22" y="192" textLength="115.2" lengthAdjust="spacing"><tspan class="d">dirty: </tspan>false</text></g>
158
158
  <g class="l43"><text xml:space="preserve" x="22" y="216" textLength="76.8" lengthAdjust="spacing"><tspan class="d">files: </tspan>1</text></g>
159
159
  <g class="l44"><text xml:space="preserve" x="22" y="240" textLength="451.2" lengthAdjust="spacing"><tspan class="d">record: </tspan>~/.done-done/evidence/notes/.done-done/</text></g>
160
- <g class="l45"><text xml:space="preserve" x="22" y="264" textLength="441.6" lengthAdjust="spacing"> runs/20261018T081521Z-396dc99b-969550.json</text></g>
160
+ <g class="l45"><text xml:space="preserve" x="22" y="264" textLength="441.6" lengthAdjust="spacing"> runs/20261020T065410Z-fedec802-12efe2.json</text></g>
161
161
  <g class="l46"><text xml:space="preserve" x="22" y="288" textLength="393.6" lengthAdjust="spacing"><tspan class="d">evidence[1]: </tspan>~/.done-done/evidence/notes/</text></g>
162
- <g class="l47"><text xml:space="preserve" x="22" y="312" textLength="441.6" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png</text></g>
162
+ <g class="l47"><text xml:space="preserve" x="22" y="312" textLength="441.6" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png</text></g>
163
163
  <g class="l48"><text xml:space="preserve" x="22" y="336" textLength="412.8" lengthAdjust="spacing"><tspan class="d">help[1]: </tspan>"Check this proof: done-done check</text></g>
164
164
  <g class="l49"><text xml:space="preserve" x="22" y="360" textLength="364.8" lengthAdjust="spacing"> --outcome \"&lt;what you proved&gt;\" ~/</text></g>
165
165
  <g class="l50"><text xml:space="preserve" x="22" y="384" textLength="288.0" lengthAdjust="spacing"> .done-done/evidence/notes/</text></g>
166
- <g class="l51"><text xml:space="preserve" x="22" y="408" textLength="451.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png"</text></g>
166
+ <g class="l51"><text xml:space="preserve" x="22" y="408" textLength="451.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png"</text></g>
167
167
  <g class="l52"><text xml:space="preserve" x="22" y="432" textLength="451.2" lengthAdjust="spacing"> synced a note on two devices, saved after.png</text></g>
168
168
  <g class="l53"><text xml:space="preserve" x="22" y="456" textLength="412.8" lengthAdjust="spacing"><tspan class="c">~/notes $ </tspan><tspan class="b">done-done check --from pr-body.md</tspan></text><g class="k53"><rect x="118" y="439" width="336.8" height="24" fill="#161b22"/><rect class="h53" x="118" y="440" width="9.6" height="20" fill="#e6edf3" opacity=".7"/></g></g>
169
169
  <g class="l54"><text xml:space="preserve" x="22" y="480" textLength="134.4" lengthAdjust="spacing"><tspan class="d">command: </tspan>check</text></g>
@@ -172,7 +172,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
172
172
  <g class="l57"><text xml:space="preserve" x="22" y="552" textLength="172.8" lengthAdjust="spacing"><tspan class="d">source: </tspan>pr-body.md</text></g>
173
173
  <g class="l58"><text xml:space="preserve" x="22" y="576" textLength="76.8" lengthAdjust="spacing"><tspan class="d">files: </tspan>1</text></g>
174
174
  <g class="l59"><text xml:space="preserve" x="22" y="600" textLength="393.6" lengthAdjust="spacing"><tspan class="d">evidence[1]: </tspan>~/.done-done/evidence/notes/</text></g>
175
- <g class="l60"><text xml:space="preserve" x="22" y="624" textLength="441.6" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png</text></g>
175
+ <g class="l60"><text xml:space="preserve" x="22" y="624" textLength="441.6" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png</text></g>
176
176
  <g class="l61"><text xml:space="preserve" x="22" y="648" textLength="57.6" lengthAdjust="spacing">judge:</text></g>
177
177
  <g class="l62"><text xml:space="preserve" x="22" y="672" textLength="249.6" lengthAdjust="spacing"> route: "answerer:gemini"</text></g>
178
178
  <g class="l63"><text xml:space="preserve" x="22" y="696" textLength="182.4" lengthAdjust="spacing"> model: demo-judge</text></g>
@@ -180,7 +180,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
180
180
  <g class="l65"><text xml:space="preserve" x="22" y="744" textLength="211.2" lengthAdjust="spacing"> author: no agent set</text></g>
181
181
  <g class="l66"><text xml:space="preserve" x="22" y="768" textLength="432.0" lengthAdjust="spacing"> judged[1]{path,verdict,reason,seen,cached}:</text></g>
182
182
  <g class="l67"><text xml:space="preserve" x="22" y="792" textLength="307.2" lengthAdjust="spacing"> ~/.done-done/evidence/notes/</text></g>
183
- <g class="l68"><text xml:space="preserve" x="22" y="816" textLength="355.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/</text></g>
183
+ <g class="l68"><text xml:space="preserve" x="22" y="816" textLength="355.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/</text></g>
184
184
  <g class="l69"><text xml:space="preserve" x="22" y="840" textLength="441.6" lengthAdjust="spacing"> after.png,pass,The screen shows the synced</text></g>
185
185
  <g class="l70"><text xml:space="preserve" x="22" y="864" textLength="355.2" lengthAdjust="spacing"> note.,The screen shows the synced</text></g>
186
186
  <g class="l71"><text xml:space="preserve" x="22" y="888" textLength="144.0" lengthAdjust="spacing"> note.,false</text></g>
@@ -195,10 +195,10 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
195
195
  <g class="l80"><text xml:space="preserve" x="22" y="120" textLength="134.4" lengthAdjust="spacing"><tspan class="d">command: </tspan>check</text></g>
196
196
  <g class="l81"><text xml:space="preserve" x="22" y="144" textLength="460.8" lengthAdjust="spacing"><tspan class="f b">error: </tspan>"check: the judge was not asked, so there</text></g>
197
197
  <g class="l82"><text xml:space="preserve" x="22" y="168" textLength="451.2" lengthAdjust="spacing"> is no verdict: ~/.done-done/evidence/notes/</text></g>
198
- <g class="l83"><text xml:space="preserve" x="22" y="192" textLength="451.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png:</text></g>
199
- <g class="l84"><text xml:space="preserve" x="22" y="216" textLength="460.8" lengthAdjust="spacing"> evidence is for commit 396dc99b6db7, HEAD is</text></g>
200
- <g class="l85"><text xml:space="preserve" x="22" y="240" textLength="422.4" lengthAdjust="spacing"> a890428671ea (changed: src/sync.js) (run</text></g>
201
- <g class="l86"><text xml:space="preserve" x="22" y="264" textLength="364.8" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550)"</text></g>
198
+ <g class="l83"><text xml:space="preserve" x="22" y="192" textLength="451.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png:</text></g>
199
+ <g class="l84"><text xml:space="preserve" x="22" y="216" textLength="460.8" lengthAdjust="spacing"> evidence is for commit fedec802045e, HEAD is</text></g>
200
+ <g class="l85"><text xml:space="preserve" x="22" y="240" textLength="422.4" lengthAdjust="spacing"> cce55aa4e026 (changed: src/sync.js) (run</text></g>
201
+ <g class="l86"><text xml:space="preserve" x="22" y="264" textLength="364.8" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2)"</text></g>
202
202
  <g class="l87"><text xml:space="preserve" x="22" y="288" textLength="432.0" lengthAdjust="spacing"><tspan class="d">help[1]: </tspan>"Commit your change, re-capture with</text></g>
203
203
  <g class="l88"><text xml:space="preserve" x="22" y="312" textLength="403.2" lengthAdjust="spacing"> done-done run sync, then check the new</text></g>
204
204
  <g class="l89"><text xml:space="preserve" x="22" y="336" textLength="96.0" lengthAdjust="spacing"> files"</text></g>
@@ -119,7 +119,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
119
119
  <g class="l4"><text xml:space="preserve" x="22" y="168" textLength="172.8" lengthAdjust="spacing"><tspan class="d">source: </tspan>pr-body.md</text></g>
120
120
  <g class="l5"><text xml:space="preserve" x="22" y="192" textLength="76.8" lengthAdjust="spacing"><tspan class="d">files: </tspan>1</text></g>
121
121
  <g class="l6"><text xml:space="preserve" x="22" y="216" textLength="393.6" lengthAdjust="spacing"><tspan class="d">evidence[1]: </tspan>~/.done-done/evidence/notes/</text></g>
122
- <g class="l7"><text xml:space="preserve" x="22" y="240" textLength="441.6" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png</text></g>
122
+ <g class="l7"><text xml:space="preserve" x="22" y="240" textLength="441.6" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png</text></g>
123
123
  <g class="l8"><text xml:space="preserve" x="22" y="264" textLength="57.6" lengthAdjust="spacing">judge:</text></g>
124
124
  <g class="l9"><text xml:space="preserve" x="22" y="288" textLength="249.6" lengthAdjust="spacing"> route: "answerer:gemini"</text></g>
125
125
  <g class="l10"><text xml:space="preserve" x="22" y="312" textLength="182.4" lengthAdjust="spacing"> model: demo-judge</text></g>
@@ -127,7 +127,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
127
127
  <g class="l12"><text xml:space="preserve" x="22" y="360" textLength="211.2" lengthAdjust="spacing"> author: no agent set</text></g>
128
128
  <g class="l13"><text xml:space="preserve" x="22" y="384" textLength="432.0" lengthAdjust="spacing"> judged[1]{path,verdict,reason,seen,cached}:</text></g>
129
129
  <g class="l14"><text xml:space="preserve" x="22" y="408" textLength="307.2" lengthAdjust="spacing"> ~/.done-done/evidence/notes/</text></g>
130
- <g class="l15"><text xml:space="preserve" x="22" y="432" textLength="355.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/</text></g>
130
+ <g class="l15"><text xml:space="preserve" x="22" y="432" textLength="355.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/</text></g>
131
131
  <g class="l16"><text xml:space="preserve" x="22" y="456" textLength="441.6" lengthAdjust="spacing"> after.png,pass,The screen shows the synced</text></g>
132
132
  <g class="l17"><text xml:space="preserve" x="22" y="480" textLength="355.2" lengthAdjust="spacing"> note.,The screen shows the synced</text></g>
133
133
  <g class="l18"><text xml:space="preserve" x="22" y="504" textLength="144.0" lengthAdjust="spacing"> note.,false</text></g>
@@ -141,10 +141,10 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
141
141
  <g class="l26"><text xml:space="preserve" x="22" y="696" textLength="134.4" lengthAdjust="spacing"><tspan class="d">command: </tspan>check</text></g>
142
142
  <g class="l27"><text xml:space="preserve" x="22" y="720" textLength="460.8" lengthAdjust="spacing"><tspan class="f b">error: </tspan>"check: the judge was not asked, so there</text></g>
143
143
  <g class="l28"><text xml:space="preserve" x="22" y="744" textLength="451.2" lengthAdjust="spacing"> is no verdict: ~/.done-done/evidence/notes/</text></g>
144
- <g class="l29"><text xml:space="preserve" x="22" y="768" textLength="451.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png:</text></g>
145
- <g class="l30"><text xml:space="preserve" x="22" y="792" textLength="460.8" lengthAdjust="spacing"> evidence is for commit 396dc99b6db7, HEAD is</text></g>
146
- <g class="l31"><text xml:space="preserve" x="22" y="816" textLength="422.4" lengthAdjust="spacing"> a890428671ea (changed: src/sync.js) (run</text></g>
147
- <g class="l32"><text xml:space="preserve" x="22" y="840" textLength="364.8" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550)"</text></g>
144
+ <g class="l29"><text xml:space="preserve" x="22" y="768" textLength="451.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png:</text></g>
145
+ <g class="l30"><text xml:space="preserve" x="22" y="792" textLength="460.8" lengthAdjust="spacing"> evidence is for commit fedec802045e, HEAD is</text></g>
146
+ <g class="l31"><text xml:space="preserve" x="22" y="816" textLength="422.4" lengthAdjust="spacing"> cce55aa4e026 (changed: src/sync.js) (run</text></g>
147
+ <g class="l32"><text xml:space="preserve" x="22" y="840" textLength="364.8" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2)"</text></g>
148
148
  <g class="l33"><text xml:space="preserve" x="22" y="864" textLength="432.0" lengthAdjust="spacing"><tspan class="d">help[1]: </tspan>"Commit your change, re-capture with</text></g>
149
149
  <g class="l34"><text xml:space="preserve" x="22" y="888" textLength="403.2" lengthAdjust="spacing"> done-done run sync, then check the new</text></g>
150
150
  <g class="l35"><text xml:space="preserve" x="22" y="912" textLength="96.0" lengthAdjust="spacing"> files"</text></g>
@@ -152,18 +152,18 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
152
152
  <g class="l37"><text xml:space="preserve" x="22" y="72" textLength="268.8" lengthAdjust="spacing"><tspan class="c">~/notes $ </tspan><tspan class="b">done-done run sync</tspan></text><g class="k37"><rect x="118" y="55" width="192.8" height="24" fill="#f6f8fa"/><rect class="h37" x="118" y="56" width="9.6" height="20" fill="#1f2328" opacity=".7"/></g></g>
153
153
  <g class="l38"><text xml:space="preserve" x="22" y="96" textLength="115.2" lengthAdjust="spacing"><tspan class="d">command: </tspan>run</text></g>
154
154
  <g class="l39"><text xml:space="preserve" x="22" y="120" textLength="153.6" lengthAdjust="spacing"><tspan class="d">result: </tspan><tspan class="p b">recorded</tspan></text></g>
155
- <g class="l40"><text xml:space="preserve" x="22" y="144" textLength="384.0" lengthAdjust="spacing"><tspan class="d">run_id: </tspan>20261018T081521Z-396dc99b-969550</text></g>
156
- <g class="l41"><text xml:space="preserve" x="22" y="168" textLength="460.8" lengthAdjust="spacing"><tspan class="d">commit: </tspan>396dc99b6db71f41bac00a43ac75439e6fb1e91c</text></g>
155
+ <g class="l40"><text xml:space="preserve" x="22" y="144" textLength="384.0" lengthAdjust="spacing"><tspan class="d">run_id: </tspan>20261020T065410Z-fedec802-12efe2</text></g>
156
+ <g class="l41"><text xml:space="preserve" x="22" y="168" textLength="460.8" lengthAdjust="spacing"><tspan class="d">commit: </tspan>fedec802045ebc823249c819dcfc195647ebad75</text></g>
157
157
  <g class="l42"><text xml:space="preserve" x="22" y="192" textLength="115.2" lengthAdjust="spacing"><tspan class="d">dirty: </tspan>false</text></g>
158
158
  <g class="l43"><text xml:space="preserve" x="22" y="216" textLength="76.8" lengthAdjust="spacing"><tspan class="d">files: </tspan>1</text></g>
159
159
  <g class="l44"><text xml:space="preserve" x="22" y="240" textLength="451.2" lengthAdjust="spacing"><tspan class="d">record: </tspan>~/.done-done/evidence/notes/.done-done/</text></g>
160
- <g class="l45"><text xml:space="preserve" x="22" y="264" textLength="441.6" lengthAdjust="spacing"> runs/20261018T081521Z-396dc99b-969550.json</text></g>
160
+ <g class="l45"><text xml:space="preserve" x="22" y="264" textLength="441.6" lengthAdjust="spacing"> runs/20261020T065410Z-fedec802-12efe2.json</text></g>
161
161
  <g class="l46"><text xml:space="preserve" x="22" y="288" textLength="393.6" lengthAdjust="spacing"><tspan class="d">evidence[1]: </tspan>~/.done-done/evidence/notes/</text></g>
162
- <g class="l47"><text xml:space="preserve" x="22" y="312" textLength="441.6" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png</text></g>
162
+ <g class="l47"><text xml:space="preserve" x="22" y="312" textLength="441.6" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png</text></g>
163
163
  <g class="l48"><text xml:space="preserve" x="22" y="336" textLength="412.8" lengthAdjust="spacing"><tspan class="d">help[1]: </tspan>"Check this proof: done-done check</text></g>
164
164
  <g class="l49"><text xml:space="preserve" x="22" y="360" textLength="364.8" lengthAdjust="spacing"> --outcome \"&lt;what you proved&gt;\" ~/</text></g>
165
165
  <g class="l50"><text xml:space="preserve" x="22" y="384" textLength="288.0" lengthAdjust="spacing"> .done-done/evidence/notes/</text></g>
166
- <g class="l51"><text xml:space="preserve" x="22" y="408" textLength="451.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png"</text></g>
166
+ <g class="l51"><text xml:space="preserve" x="22" y="408" textLength="451.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png"</text></g>
167
167
  <g class="l52"><text xml:space="preserve" x="22" y="432" textLength="451.2" lengthAdjust="spacing"> synced a note on two devices, saved after.png</text></g>
168
168
  <g class="l53"><text xml:space="preserve" x="22" y="456" textLength="412.8" lengthAdjust="spacing"><tspan class="c">~/notes $ </tspan><tspan class="b">done-done check --from pr-body.md</tspan></text><g class="k53"><rect x="118" y="439" width="336.8" height="24" fill="#f6f8fa"/><rect class="h53" x="118" y="440" width="9.6" height="20" fill="#1f2328" opacity=".7"/></g></g>
169
169
  <g class="l54"><text xml:space="preserve" x="22" y="480" textLength="134.4" lengthAdjust="spacing"><tspan class="d">command: </tspan>check</text></g>
@@ -172,7 +172,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
172
172
  <g class="l57"><text xml:space="preserve" x="22" y="552" textLength="172.8" lengthAdjust="spacing"><tspan class="d">source: </tspan>pr-body.md</text></g>
173
173
  <g class="l58"><text xml:space="preserve" x="22" y="576" textLength="76.8" lengthAdjust="spacing"><tspan class="d">files: </tspan>1</text></g>
174
174
  <g class="l59"><text xml:space="preserve" x="22" y="600" textLength="393.6" lengthAdjust="spacing"><tspan class="d">evidence[1]: </tspan>~/.done-done/evidence/notes/</text></g>
175
- <g class="l60"><text xml:space="preserve" x="22" y="624" textLength="441.6" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png</text></g>
175
+ <g class="l60"><text xml:space="preserve" x="22" y="624" textLength="441.6" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png</text></g>
176
176
  <g class="l61"><text xml:space="preserve" x="22" y="648" textLength="57.6" lengthAdjust="spacing">judge:</text></g>
177
177
  <g class="l62"><text xml:space="preserve" x="22" y="672" textLength="249.6" lengthAdjust="spacing"> route: "answerer:gemini"</text></g>
178
178
  <g class="l63"><text xml:space="preserve" x="22" y="696" textLength="182.4" lengthAdjust="spacing"> model: demo-judge</text></g>
@@ -180,7 +180,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
180
180
  <g class="l65"><text xml:space="preserve" x="22" y="744" textLength="211.2" lengthAdjust="spacing"> author: no agent set</text></g>
181
181
  <g class="l66"><text xml:space="preserve" x="22" y="768" textLength="432.0" lengthAdjust="spacing"> judged[1]{path,verdict,reason,seen,cached}:</text></g>
182
182
  <g class="l67"><text xml:space="preserve" x="22" y="792" textLength="307.2" lengthAdjust="spacing"> ~/.done-done/evidence/notes/</text></g>
183
- <g class="l68"><text xml:space="preserve" x="22" y="816" textLength="355.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/</text></g>
183
+ <g class="l68"><text xml:space="preserve" x="22" y="816" textLength="355.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/</text></g>
184
184
  <g class="l69"><text xml:space="preserve" x="22" y="840" textLength="441.6" lengthAdjust="spacing"> after.png,pass,The screen shows the synced</text></g>
185
185
  <g class="l70"><text xml:space="preserve" x="22" y="864" textLength="355.2" lengthAdjust="spacing"> note.,The screen shows the synced</text></g>
186
186
  <g class="l71"><text xml:space="preserve" x="22" y="888" textLength="144.0" lengthAdjust="spacing"> note.,false</text></g>
@@ -195,10 +195,10 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
195
195
  <g class="l80"><text xml:space="preserve" x="22" y="120" textLength="134.4" lengthAdjust="spacing"><tspan class="d">command: </tspan>check</text></g>
196
196
  <g class="l81"><text xml:space="preserve" x="22" y="144" textLength="460.8" lengthAdjust="spacing"><tspan class="f b">error: </tspan>"check: the judge was not asked, so there</text></g>
197
197
  <g class="l82"><text xml:space="preserve" x="22" y="168" textLength="451.2" lengthAdjust="spacing"> is no verdict: ~/.done-done/evidence/notes/</text></g>
198
- <g class="l83"><text xml:space="preserve" x="22" y="192" textLength="451.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png:</text></g>
199
- <g class="l84"><text xml:space="preserve" x="22" y="216" textLength="460.8" lengthAdjust="spacing"> evidence is for commit 396dc99b6db7, HEAD is</text></g>
200
- <g class="l85"><text xml:space="preserve" x="22" y="240" textLength="422.4" lengthAdjust="spacing"> a890428671ea (changed: src/sync.js) (run</text></g>
201
- <g class="l86"><text xml:space="preserve" x="22" y="264" textLength="364.8" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550)"</text></g>
198
+ <g class="l83"><text xml:space="preserve" x="22" y="192" textLength="451.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png:</text></g>
199
+ <g class="l84"><text xml:space="preserve" x="22" y="216" textLength="460.8" lengthAdjust="spacing"> evidence is for commit fedec802045e, HEAD is</text></g>
200
+ <g class="l85"><text xml:space="preserve" x="22" y="240" textLength="422.4" lengthAdjust="spacing"> cce55aa4e026 (changed: src/sync.js) (run</text></g>
201
+ <g class="l86"><text xml:space="preserve" x="22" y="264" textLength="364.8" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2)"</text></g>
202
202
  <g class="l87"><text xml:space="preserve" x="22" y="288" textLength="432.0" lengthAdjust="spacing"><tspan class="d">help[1]: </tspan>"Commit your change, re-capture with</text></g>
203
203
  <g class="l88"><text xml:space="preserve" x="22" y="312" textLength="403.2" lengthAdjust="spacing"> done-done run sync, then check the new</text></g>
204
204
  <g class="l89"><text xml:space="preserve" x="22" y="336" textLength="96.0" lengthAdjust="spacing"> files"</text></g>
@@ -16,7 +16,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
16
16
  <text xml:space="preserve" x="22" y="144" textLength="172.8" lengthAdjust="spacing"><tspan class="d">repro: </tspan>n/a: no bug</text>
17
17
  <text xml:space="preserve" x="22" y="168" textLength="412.8" lengthAdjust="spacing"><tspan class="d">proof: </tspan>no verify skill - done-done run sync</text>
18
18
  <text xml:space="preserve" x="22" y="192" textLength="364.8" lengthAdjust="spacing"><tspan class="d">evidence: </tspan>~/.done-done/evidence/notes/</text>
19
- <text xml:space="preserve" x="22" y="216" textLength="441.6" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png</text>
19
+ <text xml:space="preserve" x="22" y="216" textLength="441.6" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png</text>
20
20
  <text xml:space="preserve" x="22" y="240" textLength="412.8" lengthAdjust="spacing"><tspan class="c">~/notes $ </tspan><tspan class="b">done-done check --from pr-body.md</tspan></text>
21
21
  <text xml:space="preserve" x="22" y="264" textLength="134.4" lengthAdjust="spacing"><tspan class="d">command: </tspan>check</text>
22
22
  <text xml:space="preserve" x="22" y="288" textLength="115.2" lengthAdjust="spacing"><tspan class="d">result: </tspan><tspan class="p b">pass</tspan></text>
@@ -24,7 +24,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
24
24
  <text xml:space="preserve" x="22" y="336" textLength="172.8" lengthAdjust="spacing"><tspan class="d">source: </tspan>pr-body.md</text>
25
25
  <text xml:space="preserve" x="22" y="360" textLength="76.8" lengthAdjust="spacing"><tspan class="d">files: </tspan>1</text>
26
26
  <text xml:space="preserve" x="22" y="384" textLength="393.6" lengthAdjust="spacing"><tspan class="d">evidence[1]: </tspan>~/.done-done/evidence/notes/</text>
27
- <text xml:space="preserve" x="22" y="408" textLength="441.6" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png</text>
27
+ <text xml:space="preserve" x="22" y="408" textLength="441.6" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png</text>
28
28
  <text xml:space="preserve" x="22" y="432" textLength="57.6" lengthAdjust="spacing">judge:</text>
29
29
  <text xml:space="preserve" x="22" y="456" textLength="249.6" lengthAdjust="spacing"> route: "answerer:gemini"</text>
30
30
  <text xml:space="preserve" x="22" y="480" textLength="182.4" lengthAdjust="spacing"> model: demo-judge</text>
@@ -32,7 +32,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
32
32
  <text xml:space="preserve" x="22" y="528" textLength="211.2" lengthAdjust="spacing"> author: no agent set</text>
33
33
  <text xml:space="preserve" x="22" y="552" textLength="432.0" lengthAdjust="spacing"> judged[1]{path,verdict,reason,seen,cached}:</text>
34
34
  <text xml:space="preserve" x="22" y="576" textLength="307.2" lengthAdjust="spacing"> ~/.done-done/evidence/notes/</text>
35
- <text xml:space="preserve" x="22" y="600" textLength="355.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/</text>
35
+ <text xml:space="preserve" x="22" y="600" textLength="355.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/</text>
36
36
  <text xml:space="preserve" x="22" y="624" textLength="441.6" lengthAdjust="spacing"> after.png,pass,The screen shows the synced</text>
37
37
  <text xml:space="preserve" x="22" y="648" textLength="355.2" lengthAdjust="spacing"> note.,The screen shows the synced</text>
38
38
  <text xml:space="preserve" x="22" y="672" textLength="144.0" lengthAdjust="spacing"> note.,false</text>
@@ -16,7 +16,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
16
16
  <text xml:space="preserve" x="22" y="144" textLength="172.8" lengthAdjust="spacing"><tspan class="d">repro: </tspan>n/a: no bug</text>
17
17
  <text xml:space="preserve" x="22" y="168" textLength="412.8" lengthAdjust="spacing"><tspan class="d">proof: </tspan>no verify skill - done-done run sync</text>
18
18
  <text xml:space="preserve" x="22" y="192" textLength="364.8" lengthAdjust="spacing"><tspan class="d">evidence: </tspan>~/.done-done/evidence/notes/</text>
19
- <text xml:space="preserve" x="22" y="216" textLength="441.6" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png</text>
19
+ <text xml:space="preserve" x="22" y="216" textLength="441.6" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png</text>
20
20
  <text xml:space="preserve" x="22" y="240" textLength="412.8" lengthAdjust="spacing"><tspan class="c">~/notes $ </tspan><tspan class="b">done-done check --from pr-body.md</tspan></text>
21
21
  <text xml:space="preserve" x="22" y="264" textLength="134.4" lengthAdjust="spacing"><tspan class="d">command: </tspan>check</text>
22
22
  <text xml:space="preserve" x="22" y="288" textLength="115.2" lengthAdjust="spacing"><tspan class="d">result: </tspan><tspan class="p b">pass</tspan></text>
@@ -24,7 +24,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
24
24
  <text xml:space="preserve" x="22" y="336" textLength="172.8" lengthAdjust="spacing"><tspan class="d">source: </tspan>pr-body.md</text>
25
25
  <text xml:space="preserve" x="22" y="360" textLength="76.8" lengthAdjust="spacing"><tspan class="d">files: </tspan>1</text>
26
26
  <text xml:space="preserve" x="22" y="384" textLength="393.6" lengthAdjust="spacing"><tspan class="d">evidence[1]: </tspan>~/.done-done/evidence/notes/</text>
27
- <text xml:space="preserve" x="22" y="408" textLength="441.6" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png</text>
27
+ <text xml:space="preserve" x="22" y="408" textLength="441.6" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png</text>
28
28
  <text xml:space="preserve" x="22" y="432" textLength="57.6" lengthAdjust="spacing">judge:</text>
29
29
  <text xml:space="preserve" x="22" y="456" textLength="249.6" lengthAdjust="spacing"> route: "answerer:gemini"</text>
30
30
  <text xml:space="preserve" x="22" y="480" textLength="182.4" lengthAdjust="spacing"> model: demo-judge</text>
@@ -32,7 +32,7 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
32
32
  <text xml:space="preserve" x="22" y="528" textLength="211.2" lengthAdjust="spacing"> author: no agent set</text>
33
33
  <text xml:space="preserve" x="22" y="552" textLength="432.0" lengthAdjust="spacing"> judged[1]{path,verdict,reason,seen,cached}:</text>
34
34
  <text xml:space="preserve" x="22" y="576" textLength="307.2" lengthAdjust="spacing"> ~/.done-done/evidence/notes/</text>
35
- <text xml:space="preserve" x="22" y="600" textLength="355.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/</text>
35
+ <text xml:space="preserve" x="22" y="600" textLength="355.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/</text>
36
36
  <text xml:space="preserve" x="22" y="624" textLength="441.6" lengthAdjust="spacing"> after.png,pass,The screen shows the synced</text>
37
37
  <text xml:space="preserve" x="22" y="648" textLength="355.2" lengthAdjust="spacing"> note.,The screen shows the synced</text>
38
38
  <text xml:space="preserve" x="22" y="672" textLength="144.0" lengthAdjust="spacing"> note.,false</text>
@@ -14,10 +14,10 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
14
14
  <text xml:space="preserve" x="22" y="96" textLength="134.4" lengthAdjust="spacing"><tspan class="d">command: </tspan>check</text>
15
15
  <text xml:space="preserve" x="22" y="120" textLength="460.8" lengthAdjust="spacing"><tspan class="f b">error: </tspan>"check: the judge was not asked, so there</text>
16
16
  <text xml:space="preserve" x="22" y="144" textLength="451.2" lengthAdjust="spacing"> is no verdict: ~/.done-done/evidence/notes/</text>
17
- <text xml:space="preserve" x="22" y="168" textLength="451.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png:</text>
18
- <text xml:space="preserve" x="22" y="192" textLength="460.8" lengthAdjust="spacing"> evidence is for commit 396dc99b6db7, HEAD is</text>
19
- <text xml:space="preserve" x="22" y="216" textLength="422.4" lengthAdjust="spacing"> a890428671ea (changed: src/sync.js) (run</text>
20
- <text xml:space="preserve" x="22" y="240" textLength="364.8" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550);</text>
17
+ <text xml:space="preserve" x="22" y="168" textLength="451.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png:</text>
18
+ <text xml:space="preserve" x="22" y="192" textLength="460.8" lengthAdjust="spacing"> evidence is for commit fedec802045e, HEAD is</text>
19
+ <text xml:space="preserve" x="22" y="216" textLength="422.4" lengthAdjust="spacing"> cce55aa4e026 (changed: src/sync.js) (run</text>
20
+ <text xml:space="preserve" x="22" y="240" textLength="364.8" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2);</text>
21
21
  <text xml:space="preserve" x="22" y="264" textLength="441.6" lengthAdjust="spacing"> empty.png: empty (0 bytes); blank.png: png</text>
22
22
  <text xml:space="preserve" x="22" y="288" textLength="451.2" lengthAdjust="spacing"> is a blank frame (luminance spread 0.0000)"</text>
23
23
  <text xml:space="preserve" x="22" y="312" textLength="432.0" lengthAdjust="spacing"><tspan class="d">help[1]: </tspan>"Commit your change, re-capture with</text>
@@ -14,10 +14,10 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
14
14
  <text xml:space="preserve" x="22" y="96" textLength="134.4" lengthAdjust="spacing"><tspan class="d">command: </tspan>check</text>
15
15
  <text xml:space="preserve" x="22" y="120" textLength="460.8" lengthAdjust="spacing"><tspan class="f b">error: </tspan>"check: the judge was not asked, so there</text>
16
16
  <text xml:space="preserve" x="22" y="144" textLength="451.2" lengthAdjust="spacing"> is no verdict: ~/.done-done/evidence/notes/</text>
17
- <text xml:space="preserve" x="22" y="168" textLength="451.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png:</text>
18
- <text xml:space="preserve" x="22" y="192" textLength="460.8" lengthAdjust="spacing"> evidence is for commit 396dc99b6db7, HEAD is</text>
19
- <text xml:space="preserve" x="22" y="216" textLength="422.4" lengthAdjust="spacing"> a890428671ea (changed: src/sync.js) (run</text>
20
- <text xml:space="preserve" x="22" y="240" textLength="364.8" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550);</text>
17
+ <text xml:space="preserve" x="22" y="168" textLength="451.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png:</text>
18
+ <text xml:space="preserve" x="22" y="192" textLength="460.8" lengthAdjust="spacing"> evidence is for commit fedec802045e, HEAD is</text>
19
+ <text xml:space="preserve" x="22" y="216" textLength="422.4" lengthAdjust="spacing"> cce55aa4e026 (changed: src/sync.js) (run</text>
20
+ <text xml:space="preserve" x="22" y="240" textLength="364.8" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2);</text>
21
21
  <text xml:space="preserve" x="22" y="264" textLength="441.6" lengthAdjust="spacing"> empty.png: empty (0 bytes); blank.png: png</text>
22
22
  <text xml:space="preserve" x="22" y="288" textLength="451.2" lengthAdjust="spacing"> is a blank frame (luminance spread 0.0000)"</text>
23
23
  <text xml:space="preserve" x="22" y="312" textLength="432.0" lengthAdjust="spacing"><tspan class="d">help[1]: </tspan>"Commit your change, re-capture with</text>
@@ -13,18 +13,18 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
13
13
  <g clip-path="url(#body)"><text xml:space="preserve" x="22" y="72" textLength="268.8" lengthAdjust="spacing"><tspan class="c">~/notes $ </tspan><tspan class="b">done-done run sync</tspan></text>
14
14
  <text xml:space="preserve" x="22" y="96" textLength="115.2" lengthAdjust="spacing"><tspan class="d">command: </tspan>run</text>
15
15
  <text xml:space="preserve" x="22" y="120" textLength="153.6" lengthAdjust="spacing"><tspan class="d">result: </tspan><tspan class="p b">recorded</tspan></text>
16
- <text xml:space="preserve" x="22" y="144" textLength="384.0" lengthAdjust="spacing"><tspan class="d">run_id: </tspan>20261018T081521Z-396dc99b-969550</text>
17
- <text xml:space="preserve" x="22" y="168" textLength="460.8" lengthAdjust="spacing"><tspan class="d">commit: </tspan>396dc99b6db71f41bac00a43ac75439e6fb1e91c</text>
16
+ <text xml:space="preserve" x="22" y="144" textLength="384.0" lengthAdjust="spacing"><tspan class="d">run_id: </tspan>20261020T065410Z-fedec802-12efe2</text>
17
+ <text xml:space="preserve" x="22" y="168" textLength="460.8" lengthAdjust="spacing"><tspan class="d">commit: </tspan>fedec802045ebc823249c819dcfc195647ebad75</text>
18
18
  <text xml:space="preserve" x="22" y="192" textLength="115.2" lengthAdjust="spacing"><tspan class="d">dirty: </tspan>false</text>
19
19
  <text xml:space="preserve" x="22" y="216" textLength="76.8" lengthAdjust="spacing"><tspan class="d">files: </tspan>1</text>
20
20
  <text xml:space="preserve" x="22" y="240" textLength="451.2" lengthAdjust="spacing"><tspan class="d">record: </tspan>~/.done-done/evidence/notes/.done-done/</text>
21
- <text xml:space="preserve" x="22" y="264" textLength="441.6" lengthAdjust="spacing"> runs/20261018T081521Z-396dc99b-969550.json</text>
21
+ <text xml:space="preserve" x="22" y="264" textLength="441.6" lengthAdjust="spacing"> runs/20261020T065410Z-fedec802-12efe2.json</text>
22
22
  <text xml:space="preserve" x="22" y="288" textLength="393.6" lengthAdjust="spacing"><tspan class="d">evidence[1]: </tspan>~/.done-done/evidence/notes/</text>
23
- <text xml:space="preserve" x="22" y="312" textLength="441.6" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png</text>
23
+ <text xml:space="preserve" x="22" y="312" textLength="441.6" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png</text>
24
24
  <text xml:space="preserve" x="22" y="336" textLength="412.8" lengthAdjust="spacing"><tspan class="d">help[1]: </tspan>"Check this proof: done-done check</text>
25
25
  <text xml:space="preserve" x="22" y="360" textLength="364.8" lengthAdjust="spacing"> --outcome \"&lt;what you proved&gt;\" ~/</text>
26
26
  <text xml:space="preserve" x="22" y="384" textLength="288.0" lengthAdjust="spacing"> .done-done/evidence/notes/</text>
27
- <text xml:space="preserve" x="22" y="408" textLength="451.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png"</text>
27
+ <text xml:space="preserve" x="22" y="408" textLength="451.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png"</text>
28
28
  <text xml:space="preserve" x="22" y="432" textLength="451.2" lengthAdjust="spacing"> synced a note on two devices, saved after.png</text>
29
29
  <text xml:space="preserve" x="22" y="456" textLength="57.6" lengthAdjust="spacing"><tspan class="d">exit 0</tspan></text></g>
30
30
  </svg>
@@ -13,18 +13,18 @@ text{font:16px ui-monospace,SFMono-Regular,'SF Mono',Menlo,Consolas,'Liberation
13
13
  <g clip-path="url(#body)"><text xml:space="preserve" x="22" y="72" textLength="268.8" lengthAdjust="spacing"><tspan class="c">~/notes $ </tspan><tspan class="b">done-done run sync</tspan></text>
14
14
  <text xml:space="preserve" x="22" y="96" textLength="115.2" lengthAdjust="spacing"><tspan class="d">command: </tspan>run</text>
15
15
  <text xml:space="preserve" x="22" y="120" textLength="153.6" lengthAdjust="spacing"><tspan class="d">result: </tspan><tspan class="p b">recorded</tspan></text>
16
- <text xml:space="preserve" x="22" y="144" textLength="384.0" lengthAdjust="spacing"><tspan class="d">run_id: </tspan>20261018T081521Z-396dc99b-969550</text>
17
- <text xml:space="preserve" x="22" y="168" textLength="460.8" lengthAdjust="spacing"><tspan class="d">commit: </tspan>396dc99b6db71f41bac00a43ac75439e6fb1e91c</text>
16
+ <text xml:space="preserve" x="22" y="144" textLength="384.0" lengthAdjust="spacing"><tspan class="d">run_id: </tspan>20261020T065410Z-fedec802-12efe2</text>
17
+ <text xml:space="preserve" x="22" y="168" textLength="460.8" lengthAdjust="spacing"><tspan class="d">commit: </tspan>fedec802045ebc823249c819dcfc195647ebad75</text>
18
18
  <text xml:space="preserve" x="22" y="192" textLength="115.2" lengthAdjust="spacing"><tspan class="d">dirty: </tspan>false</text>
19
19
  <text xml:space="preserve" x="22" y="216" textLength="76.8" lengthAdjust="spacing"><tspan class="d">files: </tspan>1</text>
20
20
  <text xml:space="preserve" x="22" y="240" textLength="451.2" lengthAdjust="spacing"><tspan class="d">record: </tspan>~/.done-done/evidence/notes/.done-done/</text>
21
- <text xml:space="preserve" x="22" y="264" textLength="441.6" lengthAdjust="spacing"> runs/20261018T081521Z-396dc99b-969550.json</text>
21
+ <text xml:space="preserve" x="22" y="264" textLength="441.6" lengthAdjust="spacing"> runs/20261020T065410Z-fedec802-12efe2.json</text>
22
22
  <text xml:space="preserve" x="22" y="288" textLength="393.6" lengthAdjust="spacing"><tspan class="d">evidence[1]: </tspan>~/.done-done/evidence/notes/</text>
23
- <text xml:space="preserve" x="22" y="312" textLength="441.6" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png</text>
23
+ <text xml:space="preserve" x="22" y="312" textLength="441.6" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png</text>
24
24
  <text xml:space="preserve" x="22" y="336" textLength="412.8" lengthAdjust="spacing"><tspan class="d">help[1]: </tspan>"Check this proof: done-done check</text>
25
25
  <text xml:space="preserve" x="22" y="360" textLength="364.8" lengthAdjust="spacing"> --outcome \"&lt;what you proved&gt;\" ~/</text>
26
26
  <text xml:space="preserve" x="22" y="384" textLength="288.0" lengthAdjust="spacing"> .done-done/evidence/notes/</text>
27
- <text xml:space="preserve" x="22" y="408" textLength="451.2" lengthAdjust="spacing"> 20261018T081521Z-396dc99b-969550/after.png"</text>
27
+ <text xml:space="preserve" x="22" y="408" textLength="451.2" lengthAdjust="spacing"> 20261020T065410Z-fedec802-12efe2/after.png"</text>
28
28
  <text xml:space="preserve" x="22" y="432" textLength="451.2" lengthAdjust="spacing"> synced a note on two devices, saved after.png</text>
29
29
  <text xml:space="preserve" x="22" y="456" textLength="57.6" lengthAdjust="spacing"><tspan class="d">exit 0</tspan></text></g>
30
30
  </svg>
package/docs/reference.md CHANGED
@@ -212,7 +212,7 @@ journeys:
212
212
 
213
213
  Something entirely outside its container or the viewport is hidden on purpose (a closed drawer, a visually hidden label) and is not a hit; a cut element's children are not listed again. Content in a scrolling container (`overflow: auto` or `scroll`, such as a carousel) scrolls into view and is not clipped.
214
214
  - **What it writes.** `--out <file.png>` (default `look.png` in `$DONE_DONE_EVIDENCE_ROOT` inside a journey, else in the current folder) gets the viewport screenshot, exactly the viewport size in pixels, and `<file>.dom.json` next to it the page-check record of [DOM assertions](#dom-assertions), with the page's console errors and failed requests too. look itself fails only on the four kinds above; with a `dom` block, verify's check also reads the record and applies its allow lists to console errors and failed requests.
215
- - **Chrome.** look starts its own headless Chrome or Chromium (`google-chrome`, `google-chrome-stable`, `chromium` or `chromium-browser` on PATH, or `DONE_DONE_CHROME` naming the binary) with a throwaway profile, and removes both when it ends. It runs Chrome without its sandbox so it starts in containers and CI, so point it at your own app, not untrusted sites. No Chrome is a usage error (exit 2) that says how to install or point to one; a page that does not load (its network error, or no load event within 30 seconds) is exit 2 with the cause.
215
+ - **Chrome.** look starts its own headless Chrome or Chromium (`google-chrome`, `google-chrome-stable`, `chromium` or `chromium-browser` on PATH, or `DONE_DONE_CHROME` naming the binary) with a throwaway profile, and removes both when it ends. It runs Chrome without its sandbox so it starts in containers, so point it at your own app, not untrusted sites. No Chrome is a usage error (exit 2) that says how to install or point to one; a page that does not load (its network error, or no load event within 30 seconds) is exit 2 with the cause.
216
216
  - **Limits.** Only the main frame is measured, not an embedded frame; a transform or `clip-path` that hides part of a box is not seen; nothing is clicked or scrolled first, so drive the page to the state to check (a URL per state) before you call look. Look at the capture too: a defect the DOM cannot see (a stretched image, a colour) is the [design check](#design-check)'s.
217
217
 
218
218
  | Result | Exit |
@@ -251,7 +251,7 @@ done-done check --from pr-body.md # a file, `-` for stdin, or a
251
251
 
252
252
  `check` looks only at the files the claim names. It never scans the whole folder.
253
253
 
254
- `check` and `receipt` accept `--citation-base <dir>` for evidence paths supplied as arguments or in a Checks block, for example `done-done check --citation-base /path/to/captures --from pr-body.md`. Relative citations resolve against that directory; a relative base itself resolves against cwd. Absolute citations are unchanged. With the flag, the existing result `evidence` list contains resolved absolute paths. Without it, a relative citation resolves against the repo root first (so a check from a subdirectory finds the same evidence), then against the caller's cwd (so a shell-relative path typed for that shell still works). The flag does not relocate `--from`, change the caller repository/HEAD or config (`--trusted-ref` still selects trusted config), or change `evidence.root`: that remains the run-record, receipt and reuse-ledger store. All provenance, freshness and judge rules still apply. Passing checks preserve an explicit `--citation-base` in their suggested receipt or remote CI gate command; run the follow-up from the same cwd when the base is relative.
254
+ `check` and `receipt` accept `--citation-base <dir>` for evidence paths supplied as arguments or in a Checks block, for example `done-done check --citation-base /path/to/captures --from pr-body.md`. Relative citations resolve against that directory; a relative base itself resolves against cwd. Absolute citations are unchanged. With the flag, the existing result `evidence` list contains resolved absolute paths. Without it, a relative citation resolves against the repo root first (so a check from a subdirectory finds the same evidence), then against the caller's cwd (so a shell-relative path typed for that shell still works). The flag does not relocate `--from`, change the caller repository/HEAD or config (`--trusted-ref` still selects trusted config), or change `evidence.root`: that remains the run-record, receipt and reuse-ledger store. All provenance, freshness and judge rules still apply. Passing checks preserve an explicit `--citation-base` in their suggested receipt or merge gate command; run the follow-up from the same cwd when the base is relative.
255
255
 
256
256
  With `--from`, it reads the newest `## Checks` block:
257
257
 
@@ -354,7 +354,7 @@ done-done design shots/home.png --mock shots/home.png=design/home.png
354
354
  - **Measured strips.** A blank strip is also measured from the pixels of each PNG or JPEG screen: a band along a whole edge, at least 2% of that side deep, in one plain colour with nothing in it, and clearly apart from the screen's main colour. The measurement goes to the judge with the screen, as `measured_from_pixels` with its edge, colour and depth (`left edge: a plain #151815 band 132 px deep (12% of the width), full height, with nothing in it`); the verdict is the judge's, and the measurement never fails a screen on its own. A nav rail or bar that holds icons or a title is content, not a strip, and a screen that is all one colour is the blank-frame check's. A band its `--mock` has too (same edge, about as deep as a share of that side, and in about the same colour) is approved design and is not sent; a band the mock lacks is. If the mock cannot be measured, a band on the screen goes to the judge as one the mock could not be measured to compare. A fully transparent pixel is never measured as part of a band, so a transparent margin is not a strip on a light or dark screen. The measure misses a strip close to the screen's main colour (a black bar on a near-black screen); the judge's question still covers it.
355
355
  - **Mocks.** `--mock <screen>=<mock>` sends the screen and its approved mock in the same request, and the judge reports a difference from the mock as a defect of its kind (a moved, missing or resized element is `layout`); live data such as names, numbers and times is not a defect, and an element shaped as in the mock is not stretched. A dialog, sheet, menu, popover or toast is not a defect only where the mock draws that same overlay in the same place; one the mock lacks, or draws elsewhere, is judged like any other element. Repeat `--mock` per screen; a screen named only in `--mock` is still checked. Without a mock, the screen is judged alone.
356
356
  - **Variants.** `design` judges the screens it is given and captures nothing. Capture each variant your change touches (dark theme, small width, large font) as its own screen and pass them all; a name such as `home.dark.png` or `home.large-font.png` keeps them apart, and each variant takes its own `--mock`. Pass the raw capture: the judge reads everything in the image as the app, so a device frame or letterbox band fails as a `blank-strip`.
357
- - **From a verify flow.** Run it on the screens a journey writes, after the capture, so a failing screen fails the journey and the `verify` run with it: `journeys: { shots: { command: "node capture.mjs && done-done design $DONE_DONE_EVIDENCE_ROOT/*.png" } }`. Or run it as its own CI step on the captured screens.
357
+ - **From a verify flow.** Run it on the screens a journey writes, after the capture, so a failing screen fails the journey and the `verify` run with it: `journeys: { shots: { command: "node capture.mjs && done-done design $DONE_DONE_EVIDENCE_ROOT/*.png" } }`.
358
358
 
359
359
  | Verdict | Result | Exit |
360
360
  |---|---|---|
@@ -367,7 +367,7 @@ The result lists each screen (failing first, then unsure, then passing; `--full`
367
367
 
368
368
  ### Record a receipt
369
369
 
370
- A receipt ties one passing check to one commit and to the exact bytes it cited, so a merge step or CI can re-verify the same proof later.
370
+ A receipt ties one passing check to one commit and to the exact bytes it cited, so a merge gate (or `gate` in CI) can re-verify the same proof later.
371
371
 
372
372
  ```sh
373
373
  done-done receipt --from pr-body.md # or --outcome "<claim>" <files...>, or --from -
@@ -381,7 +381,7 @@ done-done receipt --verify <head> # read-only: check a recorded receipt
381
381
  ### Verify a change in one run
382
382
 
383
383
  ```sh
384
- done-done verify [<journey>] --outcome "<what a user can now do>" # --no-wait, --wait (the default), or --foreground (CI)
384
+ done-done verify [<journey>] --outcome "<what a user can now do>" # --no-wait, --wait (the default), or --foreground (for scripts)
385
385
  done-done verify --outcome "<claim>" [--feature <name>] # no journey: the author agent drives a verify skill feature
386
386
  done-done respond [--run <run-id>] --action fix | --action approve|skip --reason "<why>" # answer a run at its gate (approve and skip: a spend gate only)
387
387
  done-done status [--run <run-id>] # the newest run of this checkout by default
@@ -445,7 +445,7 @@ npx -y sign-off-axi post --kind <gate|evidence> --from <file> # the command si
445
445
  ### Keep the verify skill true
446
446
 
447
447
  ```sh
448
- done-done maintain # --no-wait, --wait (the default), or --foreground (CI)
448
+ done-done maintain # --no-wait, --wait (the default), or --foreground (for scripts)
449
449
  ```
450
450
 
451
451
  `maintain` checks the repo's verify skill against the source in a run of kind `maintain`, in the same store as `verify`, so `status`, `attach`, `logs`, `abort` and `history` work on it. Its pass reads only: it never edits the skill, the product code or git, and it records HEAD, `git status --porcelain --untracked-files=all` and the content of each changed or untracked path (regular files are hashed with sha256 as they are read; symlinks are compared by target text and never read through) before and after; if they differ, the run is `blocked` with a `repo-changed` finding. Its steps:
@@ -482,7 +482,7 @@ Running `done-done` with no subcommand prints the repo's config state (`config:
482
482
 
483
483
  `doctor` exits 2 when a required item (`git`, `config`, `evidence_root`, `agent`, `judge`, and `probe` with `--probe`) is missing.
484
484
 
485
- ### In CI or a merge gate
485
+ ### Merge gate on the desktop
486
486
 
487
487
  ```sh
488
488
  PR_BRANCH="$(gh pr view "$PR_URL" --json headRefName --jq .headRefName)"
@@ -491,14 +491,35 @@ done-done receipt --from - --trusted-ref origin/main --claim-id "$PR_BRANCH" < p
491
491
  done-done receipt --verify "$HEAD" # re-verify later
492
492
  ```
493
493
 
494
- Run `receipt --verify` with that head checked out and the evidence root in place (see below). `--trusted-ref` reads `.done-done.yaml` from that ref instead of the PR branch. This matters because repo keys can loosen the bar and journeys run commands. `check --from -` and `receipt --from -` read the `## Checks` block from stdin, so a CI job can pipe the pull request body straight in.
494
+ Run `receipt --verify` with that head checked out and the evidence root in place (see below). `--trusted-ref` reads `.done-done.yaml` from that ref instead of the PR branch. This matters because repo keys can loosen the bar and journeys run commands. `check --from -` and `receipt --from -` read the `## Checks` block from stdin, so a script can pipe the pull request body straight in.
495
+
496
+ ### CI gate
497
+
498
+ A hosted runner has no model and never gets a model key, so CI never runs `check`, `run` or `verify`. Proof runs on the desktop. CI runs one no-model command, `done-done gate`, which reads git objects only: no config, no judge, no key and no evidence files.
499
+
500
+ ```sh
501
+ # on the desktop, after the proof passes for <head>
502
+ done-done receipt --from checks.md --claim-id "$BRANCH" # writes <evidence root>/.done-done/receipts/<head>.json
503
+ mkdir -p .done-done-receipts && cp <that receipt> .done-done-receipts/
504
+ git add .done-done-receipts && git commit -m "chore: receipt for <head>" && git push
505
+
506
+ # in CI (checkout with fetch-depth: 0, so the receipt head is in history)
507
+ npx -y done-done-axi gate "$PR_HEAD_SHA"
508
+ ```
509
+
510
+ The receipt is committed in a follow-up commit, because a receipt cannot sit inside the commit it proves (its hash would change). `gate [<head>] [--dir <folder>] [--pushed <time>]` prints `PASS` or `FAIL: <reason>` and exits 0, 1, or 2 on a usage error. It passes when a receipt in `.done-done-receipts/` (or `--dir`):
511
+
512
+ - is a well-formed pass from a judged run (the same shape `receipt --verify` requires) and is filed as `<its head>.json`;
513
+ - has a head that is `<head>` (default `HEAD`) or an ancestor of it, with every file changed since that head inside the receipts folder, so the proof still covers the code;
514
+ - was made no later than the push (`--pushed`, an ISO time; default the commit time of `<head>`), compared to the second.
515
+
516
+ `gate` trusts the committed JSON: it does not re-hash the evidence, which only `receipt --verify` can do, on the machine that holds it. It says so by name and does not claim more.
495
517
 
496
518
  ### Where proof lives
497
519
 
498
520
  Evidence, run records (`<root>/.done-done/runs/`), receipts (`<root>/.done-done/receipts/<head>.json`) and the reuse ledger (`<root>/.done-done/ledger`) all live in the evidence root. By default that is `~/.done-done/evidence/<app>`, outside the repo, so they outlive the checkout that captured them: any checkout of the same repo on the same machine (a fresh clone, a merge gate's checkout) with `<head>` checked out runs `done-done receipt --verify <head>` and finds the same receipt and files. A checkout of a later commit, such as the merge or squash commit on main, does not verify: the receipt is for `<head>` only.
499
521
 
500
522
  - **Same machine** (a merge gate, a fresh clone): check out `<head>`, nothing else. Every checkout reads the same `app` from the committed `.done-done.yaml`, so it resolves the same evidence root. Keep `app` pinned (`init` writes it); without it the app is the directory name, which differs between clones. Receipts are named by the full head sha, which is the same in every clone, shallow or not, so no repo commit is needed in the key, and a receipt verifies only where that commit is checked out.
501
- - **CI on another machine**: the evidence is not there. Copy the evidence root to the job (for example upload `~/.done-done/evidence/<app>` as a build artifact from the job that captured the proof), restore it as `$DONE_DONE_HOME/evidence/<app>`, check out the head, and run `done-done receipt --verify <head>`.
502
523
  - **`evidence.root` inside the repo** (an older `root: .proof`): still works, but the evidence is gitignored and goes away with that checkout, so only that checkout can re-verify.
503
524
 
504
525
  ## Config
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "done-done-axi",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Proof that the real product works before a task is called done: commit-bound evidence runs and evidence checks.",
6
6
  "type": "module",
@@ -29,7 +29,9 @@ First run `command -v done-done`. If it prints a path, run `done-done` as shown
29
29
  - `done-done <command> --help` for one command's usage and flags
30
30
 
31
31
  Results are TOON: read `result` and follow the `help` lines through
32
- `init`, `doctor`, `run`, `verify`, `check` and `receipt`. Proof comes from driving the real product, not from a unit test run, and counts only from a committed
32
+ `init`, `doctor`, `run`, `verify`, `check` and `receipt`. `design` judges captured screens for visual defects,
33
+ `maintain` keeps the repo's verify skill true, and `signoff` writes a run's gate or evidence as a sign-off message
34
+ (it prints the post command and never posts). Proof comes from driving the real product, not from a unit test run, and counts only from a committed
33
35
  tree: commit the done-done setup (config, capture script) before you capture. `fail` or
34
36
  `inconclusive` is not done: fix the cause and re-run, or tell the user exactly what is still unproven.
35
37
  A run waiting at its judge gate (`awaiting_approval`) did not pass, and nothing moves it past its judge: show the user
@@ -51,6 +53,13 @@ with no judge or model and prints `PASS` or `FAIL: <reason>` (exit 1 on a fail):
51
53
  empty, stale, duplicated or already used by another PR, a n/a repro on a fix, or a PR of 1,000+ added lines all fail.
52
54
  `evidence: not proven: <cause>` passes. Fix the cause it names and re-run; never edit the evidence to get past it.
53
55
 
56
+ A CI job gets no model and no key, so it never runs `check`, `run` or `verify`. Proof runs on the desktop, then commit the
57
+ receipt it wrote (`<evidence root>/.done-done/receipts/<head>.json`) into `.done-done-receipts/` in a follow-up commit. CI runs only
58
+ `done-done gate <head>` (full history): `PASS` or `FAIL: <reason>` for a missing, late, other-head or moved-on receipt.
59
+
60
+ `init` also has that agent write the repo's `.agents/skills/verify-<app>/SKILL.md` (never overwriting one): read it before
61
+ you drive the app, and fix it when it is wrong.
62
+
54
63
  done-done starts a coding agent the user chose. When it says no agent is set, ask the user which agent
55
64
  (and model) to use and save their answer with the command it prints; never choose one for them.
56
65
 
@@ -1,7 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="362" height="64" viewBox="0 0 362 64" role="img" aria-label="done-done">
2
- <title>done-done</title>
3
- <defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#3fb950"/><stop offset="1" stop-color="#1a7f37"/></linearGradient></defs>
4
- <rect width="64" height="64" rx="16" fill="url(#g)"/>
5
- <g fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"><path d="M13 34l9 9 17-20"/><path d="M33 40l3 3 17-20"/></g>
6
- <g transform="translate(86 49)"><path fill="#e6edf3" d="M14 0.6Q9.5 0.6 6.7 -2.6Q4 -5.8 4 -11.2V-19.5Q4 -25 6.7 -28.2Q9.5 -31.4 14 -31.4Q17.2 -31.4 19.4 -29.7Q21.6 -28.1 22.5 -25.2L22.3 -32.3V-40.9H29.3V0H22.5V-5.6Q21.6 -2.7 19.4 -1.1Q17.2 0.6 14 0.6ZM16.6 -5.5Q19.3 -5.5 20.8 -7.1Q22.3 -8.7 22.3 -11.7V-19.1Q22.3 -22.1 20.8 -23.7Q19.3 -25.3 16.6 -25.3Q13.9 -25.3 12.5 -23.9Q11 -22.4 11 -19.4V-11.4Q11 -8.4 12.5 -6.9Q13.9 -5.5 16.6 -5.5ZM46.9 0.5Q41 0.5 37.5 -2.8Q34 -6.2 34 -11.8V-19Q34 -24.7 37.5 -28Q41 -31.3 46.9 -31.3Q52.8 -31.3 56.3 -28Q59.8 -24.7 59.8 -19V-11.8Q59.8 -6.2 56.3 -2.8Q52.8 0.5 46.9 0.5ZM46.9 -5.6Q49.7 -5.6 51.2 -7.1Q52.8 -8.7 52.8 -11.6V-19.2Q52.8 -22.1 51.2 -23.7Q49.7 -25.2 46.9 -25.2Q44.1 -25.2 42.6 -23.7Q41 -22.1 41 -19.2V-11.6Q41 -8.7 42.6 -7.1Q44.1 -5.6 46.9 -5.6ZM64.5 0V-30.8H71.3V-25Q71.9 -27.9 74.1 -29.7Q76.3 -31.4 79.6 -31.4Q84.2 -31.4 86.9 -28.3Q89.6 -25.3 89.6 -20.2V0H82.6V-19.4Q82.6 -22.2 81.1 -23.8Q79.6 -25.3 77.1 -25.3Q74.5 -25.3 73 -23.7Q71.5 -22.2 71.5 -19.3V0ZM107.2 0.6Q103.2 0.6 100.3 -1Q97.4 -2.5 95.8 -5.2Q94.2 -8 94.2 -11.8V-19Q94.2 -22.8 95.8 -25.6Q97.4 -28.3 100.3 -29.8Q103.2 -31.4 107.2 -31.4Q111 -31.4 113.9 -29.8Q116.8 -28.3 118.4 -25.6Q120 -22.9 120 -19.3V-13.7H100.9V-11.5Q100.9 -8.4 102.6 -6.8Q104.2 -5.2 107.2 -5.2Q109.3 -5.2 110.9 -6Q112.4 -6.7 112.7 -8.2H119.6Q118.9 -4.2 115.5 -1.8Q112.1 0.6 107.2 0.6ZM100.9 -19.3V-18.2L113.3 -18.3V-19.3Q113.3 -22.5 111.7 -24.2Q110.1 -26 107.2 -26Q104.1 -26 102.5 -24.2Q100.9 -22.5 100.9 -19.3ZM128.2 -15.2V-21.7H146.2V-15.2Z"/><path fill="#3fb950" d="M164.5 0.6Q160 0.6 157.2 -2.6Q154.5 -5.8 154.5 -11.2V-19.5Q154.5 -25 157.2 -28.2Q160 -31.4 164.5 -31.4Q167.7 -31.4 169.9 -29.7Q172.1 -28.1 173 -25.2L172.8 -32.3V-40.9H179.8V0H173V-5.6Q172.1 -2.7 169.9 -1.1Q167.7 0.6 164.5 0.6ZM167.1 -5.5Q169.8 -5.5 171.3 -7.1Q172.8 -8.7 172.8 -11.7V-19.1Q172.8 -22.1 171.3 -23.7Q169.8 -25.3 167.1 -25.3Q164.4 -25.3 163 -23.9Q161.5 -22.4 161.5 -19.4V-11.4Q161.5 -8.4 163 -6.9Q164.4 -5.5 167.1 -5.5ZM197.4 0.5Q191.5 0.5 188 -2.8Q184.5 -6.2 184.5 -11.8V-19Q184.5 -24.7 188 -28Q191.5 -31.3 197.4 -31.3Q203.3 -31.3 206.8 -28Q210.3 -24.7 210.3 -19V-11.8Q210.3 -6.2 206.8 -2.8Q203.3 0.5 197.4 0.5ZM197.4 -5.6Q200.2 -5.6 201.7 -7.1Q203.3 -8.7 203.3 -11.6V-19.2Q203.3 -22.1 201.7 -23.7Q200.2 -25.2 197.4 -25.2Q194.6 -25.2 193.1 -23.7Q191.5 -22.1 191.5 -19.2V-11.6Q191.5 -8.7 193.1 -7.1Q194.6 -5.6 197.4 -5.6ZM215 0V-30.8H221.8V-25Q222.4 -27.9 224.6 -29.7Q226.8 -31.4 230.1 -31.4Q234.7 -31.4 237.4 -28.3Q240.1 -25.3 240.1 -20.2V0H233.1V-19.4Q233.1 -22.2 231.6 -23.8Q230.1 -25.3 227.6 -25.3Q225 -25.3 223.5 -23.7Q222 -22.2 222 -19.3V0ZM257.7 0.6Q253.7 0.6 250.8 -1Q247.9 -2.5 246.3 -5.2Q244.7 -8 244.7 -11.8V-19Q244.7 -22.8 246.3 -25.6Q247.9 -28.3 250.8 -29.8Q253.7 -31.4 257.7 -31.4Q261.5 -31.4 264.4 -29.8Q267.3 -28.3 268.9 -25.6Q270.5 -22.9 270.5 -19.3V-13.7H251.4V-11.5Q251.4 -8.4 253.1 -6.8Q254.7 -5.2 257.7 -5.2Q259.8 -5.2 261.4 -6Q262.9 -6.7 263.2 -8.2H270.1Q269.4 -4.2 266 -1.8Q262.6 0.6 257.7 0.6ZM251.4 -19.3V-18.2L263.8 -18.3V-19.3Q263.8 -22.5 262.2 -24.2Q260.6 -26 257.7 -26Q254.6 -26 253 -24.2Q251.4 -22.5 251.4 -19.3Z"/></g>
7
- </svg>
@@ -1,7 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="362" height="64" viewBox="0 0 362 64" role="img" aria-label="done-done">
2
- <title>done-done</title>
3
- <defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#3fb950"/><stop offset="1" stop-color="#1a7f37"/></linearGradient></defs>
4
- <rect width="64" height="64" rx="16" fill="url(#g)"/>
5
- <g fill="none" stroke="#fff" stroke-width="6" stroke-linecap="round" stroke-linejoin="round"><path d="M13 34l9 9 17-20"/><path d="M33 40l3 3 17-20"/></g>
6
- <g transform="translate(86 49)"><path fill="#1f2328" d="M14 0.6Q9.5 0.6 6.7 -2.6Q4 -5.8 4 -11.2V-19.5Q4 -25 6.7 -28.2Q9.5 -31.4 14 -31.4Q17.2 -31.4 19.4 -29.7Q21.6 -28.1 22.5 -25.2L22.3 -32.3V-40.9H29.3V0H22.5V-5.6Q21.6 -2.7 19.4 -1.1Q17.2 0.6 14 0.6ZM16.6 -5.5Q19.3 -5.5 20.8 -7.1Q22.3 -8.7 22.3 -11.7V-19.1Q22.3 -22.1 20.8 -23.7Q19.3 -25.3 16.6 -25.3Q13.9 -25.3 12.5 -23.9Q11 -22.4 11 -19.4V-11.4Q11 -8.4 12.5 -6.9Q13.9 -5.5 16.6 -5.5ZM46.9 0.5Q41 0.5 37.5 -2.8Q34 -6.2 34 -11.8V-19Q34 -24.7 37.5 -28Q41 -31.3 46.9 -31.3Q52.8 -31.3 56.3 -28Q59.8 -24.7 59.8 -19V-11.8Q59.8 -6.2 56.3 -2.8Q52.8 0.5 46.9 0.5ZM46.9 -5.6Q49.7 -5.6 51.2 -7.1Q52.8 -8.7 52.8 -11.6V-19.2Q52.8 -22.1 51.2 -23.7Q49.7 -25.2 46.9 -25.2Q44.1 -25.2 42.6 -23.7Q41 -22.1 41 -19.2V-11.6Q41 -8.7 42.6 -7.1Q44.1 -5.6 46.9 -5.6ZM64.5 0V-30.8H71.3V-25Q71.9 -27.9 74.1 -29.7Q76.3 -31.4 79.6 -31.4Q84.2 -31.4 86.9 -28.3Q89.6 -25.3 89.6 -20.2V0H82.6V-19.4Q82.6 -22.2 81.1 -23.8Q79.6 -25.3 77.1 -25.3Q74.5 -25.3 73 -23.7Q71.5 -22.2 71.5 -19.3V0ZM107.2 0.6Q103.2 0.6 100.3 -1Q97.4 -2.5 95.8 -5.2Q94.2 -8 94.2 -11.8V-19Q94.2 -22.8 95.8 -25.6Q97.4 -28.3 100.3 -29.8Q103.2 -31.4 107.2 -31.4Q111 -31.4 113.9 -29.8Q116.8 -28.3 118.4 -25.6Q120 -22.9 120 -19.3V-13.7H100.9V-11.5Q100.9 -8.4 102.6 -6.8Q104.2 -5.2 107.2 -5.2Q109.3 -5.2 110.9 -6Q112.4 -6.7 112.7 -8.2H119.6Q118.9 -4.2 115.5 -1.8Q112.1 0.6 107.2 0.6ZM100.9 -19.3V-18.2L113.3 -18.3V-19.3Q113.3 -22.5 111.7 -24.2Q110.1 -26 107.2 -26Q104.1 -26 102.5 -24.2Q100.9 -22.5 100.9 -19.3ZM128.2 -15.2V-21.7H146.2V-15.2Z"/><path fill="#1a7f37" d="M164.5 0.6Q160 0.6 157.2 -2.6Q154.5 -5.8 154.5 -11.2V-19.5Q154.5 -25 157.2 -28.2Q160 -31.4 164.5 -31.4Q167.7 -31.4 169.9 -29.7Q172.1 -28.1 173 -25.2L172.8 -32.3V-40.9H179.8V0H173V-5.6Q172.1 -2.7 169.9 -1.1Q167.7 0.6 164.5 0.6ZM167.1 -5.5Q169.8 -5.5 171.3 -7.1Q172.8 -8.7 172.8 -11.7V-19.1Q172.8 -22.1 171.3 -23.7Q169.8 -25.3 167.1 -25.3Q164.4 -25.3 163 -23.9Q161.5 -22.4 161.5 -19.4V-11.4Q161.5 -8.4 163 -6.9Q164.4 -5.5 167.1 -5.5ZM197.4 0.5Q191.5 0.5 188 -2.8Q184.5 -6.2 184.5 -11.8V-19Q184.5 -24.7 188 -28Q191.5 -31.3 197.4 -31.3Q203.3 -31.3 206.8 -28Q210.3 -24.7 210.3 -19V-11.8Q210.3 -6.2 206.8 -2.8Q203.3 0.5 197.4 0.5ZM197.4 -5.6Q200.2 -5.6 201.7 -7.1Q203.3 -8.7 203.3 -11.6V-19.2Q203.3 -22.1 201.7 -23.7Q200.2 -25.2 197.4 -25.2Q194.6 -25.2 193.1 -23.7Q191.5 -22.1 191.5 -19.2V-11.6Q191.5 -8.7 193.1 -7.1Q194.6 -5.6 197.4 -5.6ZM215 0V-30.8H221.8V-25Q222.4 -27.9 224.6 -29.7Q226.8 -31.4 230.1 -31.4Q234.7 -31.4 237.4 -28.3Q240.1 -25.3 240.1 -20.2V0H233.1V-19.4Q233.1 -22.2 231.6 -23.8Q230.1 -25.3 227.6 -25.3Q225 -25.3 223.5 -23.7Q222 -22.2 222 -19.3V0ZM257.7 0.6Q253.7 0.6 250.8 -1Q247.9 -2.5 246.3 -5.2Q244.7 -8 244.7 -11.8V-19Q244.7 -22.8 246.3 -25.6Q247.9 -28.3 250.8 -29.8Q253.7 -31.4 257.7 -31.4Q261.5 -31.4 264.4 -29.8Q267.3 -28.3 268.9 -25.6Q270.5 -22.9 270.5 -19.3V-13.7H251.4V-11.5Q251.4 -8.4 253.1 -6.8Q254.7 -5.2 257.7 -5.2Q259.8 -5.2 261.4 -6Q262.9 -6.7 263.2 -8.2H270.1Q269.4 -4.2 266 -1.8Q262.6 0.6 257.7 0.6ZM251.4 -19.3V-18.2L263.8 -18.3V-19.3Q263.8 -22.5 262.2 -24.2Q260.6 -26 257.7 -26Q254.6 -26 253 -24.2Q251.4 -22.5 251.4 -19.3Z"/></g>
7
- </svg>