done-done-axi 0.4.1 → 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 +42 -13
- package/dist/cli.js +142 -21
- package/dist/lint.js +125 -0
- package/dist/look.js +1 -1
- package/docs/logo.svg +1 -0
- package/docs/media/hero-dark.svg +17 -17
- package/docs/media/hero-light.svg +17 -17
- package/docs/media/pass-dark.svg +3 -3
- package/docs/media/pass-light.svg +3 -3
- package/docs/media/reasons-dark.svg +4 -4
- package/docs/media/reasons-light.svg +4 -4
- package/docs/media/run-dark.svg +5 -5
- package/docs/media/run-light.svg +5 -5
- package/docs/reference.md +41 -9
- package/package.json +1 -1
- package/skills/done-done/SKILL.md +15 -1
- package/docs/media/wordmark-dark.svg +0 -7
- package/docs/media/wordmark-light.svg +0 -7
package/README.md
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
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
|
[](https://nodejs.org) [](#whats-next) [](https://www.npmjs.com/package/done-done-axi)
|
|
13
12
|
|
|
14
|
-
|
|
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
|
|
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
|
|
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.
|
|
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,17 +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)
|
|
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)).
|
|
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)).
|
|
165
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)).
|
|
166
|
-
- 🔒 **Safe in CI**: `--trusted-ref origin/main` reads the rules from your main branch, so a pull request can't loosen them.
|
|
167
185
|
|
|
168
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.
|
|
169
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
|
+
|
|
170
196
|
## Use with Firstmate or another agent fleet
|
|
171
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
|
+
|
|
172
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/).
|
|
173
202
|
|
|
174
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:
|
|
@@ -194,7 +223,7 @@ Another fleet works the same way: add the include text to whatever your workers
|
|
|
194
223
|
|
|
195
224
|
## What's next
|
|
196
225
|
|
|
197
|
-
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.
|
|
198
227
|
|
|
199
228
|
## Develop
|
|
200
229
|
|
|
@@ -205,7 +234,7 @@ npm install
|
|
|
205
234
|
npm install -g --prefix ~/.local . # optional: links this checkout as done-done on your PATH
|
|
206
235
|
npm test # builds, then runs the end-to-end tests against throwaway git repos
|
|
207
236
|
npm run build:skill # regenerate skills/done-done/SKILL.md from src/skill.ts
|
|
208
|
-
npm run build:skill -- --check # fail if the committed skill is out of date
|
|
237
|
+
npm run build:skill -- --check # fail if the committed skill is out of date
|
|
209
238
|
```
|
|
210
239
|
|
|
211
240
|
To try an unreleased commit without cloning, run it straight from GitHub (npm 12 needs `--allow-git=all`):
|
|
@@ -216,4 +245,4 @@ npx -y --allow-git=all github:umeranjum17/done-done-axi --help
|
|
|
216
245
|
|
|
217
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.
|
|
218
247
|
|
|
219
|
-
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
|
|
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
|
@@ -18,6 +18,7 @@ import { cellEnv, cellName, cellSuffix, hasMatrix, matrixManifest, splitMatrix }
|
|
|
18
18
|
import { design, KINDS } from './design.js';
|
|
19
19
|
import { checkMedia } from './media.js';
|
|
20
20
|
import { domIssues, domRecord, isDomRecord, lookIssues, SQUASHED_TEXT } from './dom.js';
|
|
21
|
+
import { lint } from './lint.js';
|
|
21
22
|
import { look } from './look.js';
|
|
22
23
|
import { scanSecrets } from './secrets.js';
|
|
23
24
|
const PASS = 0, FAIL = 1, USAGE = 2, INCONCLUSIVE = 3, WAITING = 4;
|
|
@@ -92,7 +93,7 @@ const FLAGS = {
|
|
|
92
93
|
'--close <run-id>': 'seal an open run and record the files in its folder',
|
|
93
94
|
'--outcome "<claim>"': 'the outcome the evidence files prove; the files follow as arguments',
|
|
94
95
|
'--from <file | - | PR URL>': 'read the claim from the newest "## Checks" block of a file, stdin or a PR body',
|
|
95
|
-
'--trusted-ref <ref>': 'read .done-done.yaml from this git ref instead of the working tree (
|
|
96
|
+
'--trusted-ref <ref>': 'read .done-done.yaml from this git ref instead of the working tree (for scripts)',
|
|
96
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',
|
|
97
98
|
'--citation-base <dir>': 'resolve evidence citations against this directory (default: cwd); absolute citations stay unchanged; does not change repo, config or run storage',
|
|
98
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)',
|
|
@@ -100,7 +101,7 @@ const FLAGS = {
|
|
|
100
101
|
'--outcome "<what a user can now do>"': 'the outcome the run must prove',
|
|
101
102
|
'--wait': 'wait until the run ends (the default)',
|
|
102
103
|
'--no-wait': 'start the run and return at once (exit 4); read it later with done-done status',
|
|
103
|
-
'--foreground': 'run every step in this process instead of a detached runner (
|
|
104
|
+
'--foreground': 'run every step in this process instead of a detached runner (for scripts)',
|
|
104
105
|
'--run <run-id>': 'the run to show (default: the newest run of this checkout)',
|
|
105
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)',
|
|
106
107
|
'--mock <screen>=<mock>': 'judge this screen next to its approved mock (repeat per screen; the screen is judged even when not listed)',
|
|
@@ -173,7 +174,7 @@ const COMMANDS = {
|
|
|
173
174
|
usage: [
|
|
174
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'],
|
|
175
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'],
|
|
176
|
-
['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
|
|
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'],
|
|
177
178
|
],
|
|
178
179
|
flags: ['--outcome "<what a user can now do>"', '--feature <name>', '--claim-id <id>', '--wait', '--no-wait', '--foreground'],
|
|
179
180
|
more: [RUNS_TEXT],
|
|
@@ -197,6 +198,26 @@ const COMMANDS = {
|
|
|
197
198
|
overflow, squashed text, text clipped by its container and elements cut off by the left, right or top edge, writes the
|
|
198
199
|
screenshot and its .dom.json record, and exits 1 naming each element, kind and text, 0 when there is none. A hit always
|
|
199
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`],
|
|
210
|
+
},
|
|
211
|
+
lint: {
|
|
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']],
|
|
213
|
+
flags: [],
|
|
214
|
+
more: [`lint: a deterministic gate that is on whenever you call it. The newest "## Checks" block of the PR body (gh pr view) or report file needs
|
|
215
|
+
outcome:, repro:, proof: and evidence: lines. proof names a command or path, and the repo's verify-<app> skill, or says "no verify skill"
|
|
216
|
+
when the repo (the current folder) has none; repro is not n/a on a fix PR. evidence names files in the evidence folder (or absolute):
|
|
217
|
+
each exists, is non-empty and is newer than the folder itself; no two are byte-identical; at least one has a hash no other PR used
|
|
218
|
+
(hash store lint.hashes in ~/.done-done, or $DONE_DONE_HOME). "evidence: not proven: <cause>" passes. A PR with 1,000 or more added
|
|
219
|
+
non-generated lines fails (lockfiles, vendor/, dist/, LICENSE-type files, deletions and moved lines do not count); a bot PR passes.
|
|
220
|
+
Every run is appended to lint.log next to the store. Exit 0 PASS, 1 FAIL, 2 usage error. Needs gh for a PR URL`],
|
|
200
221
|
},
|
|
201
222
|
status: {
|
|
202
223
|
usage: [['done-done status [--run <run-id>]', "show a run's steps, result, findings and next step"]],
|
|
@@ -560,7 +581,7 @@ function claimFromChecks(body, title, repo, cfg, out) {
|
|
|
560
581
|
out.push({ level: 'fail', why: 'evidence names no files' });
|
|
561
582
|
return { outcome: f.outcome, files, title };
|
|
562
583
|
}
|
|
563
|
-
// A PR body also names its head branch, which is the claim a
|
|
584
|
+
// A PR body also names its head branch, which is the claim a detached-HEAD checkout is checking.
|
|
564
585
|
function readSource(from) {
|
|
565
586
|
if (from === '-')
|
|
566
587
|
return { body: readFileSync(0, 'utf8') };
|
|
@@ -811,7 +832,7 @@ async function cmdCheck(argv) {
|
|
|
811
832
|
}
|
|
812
833
|
const receiptsDir = (cfg) => join(cfg.evidence.root, '.done-done', 'receipts');
|
|
813
834
|
// A receipt names a cited file relative to the repo, or to an evidence root outside it under this prefix,
|
|
814
|
-
// so any checkout of the repo
|
|
835
|
+
// so any checkout of the repo finds the same file.
|
|
815
836
|
const IN_EVIDENCE = '<evidence root>/';
|
|
816
837
|
function receiptPath(repo, cfg, p) {
|
|
817
838
|
const inRepo = under(repo, p);
|
|
@@ -827,6 +848,17 @@ function writeReceipt(cfg, rec) {
|
|
|
827
848
|
renameSync(`${path}.tmp`, path);
|
|
828
849
|
return path;
|
|
829
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
|
+
}
|
|
830
862
|
// Verify a recorded receipt for `headArg`, which may be a full sha or a ref (HEAD, a branch or a tag).
|
|
831
863
|
// Read-only, so a missing or changed record is a failure, not a usage error.
|
|
832
864
|
function verifyReceipt(repo, cfg, headArg) {
|
|
@@ -849,16 +881,10 @@ function verifyReceipt(repo, cfg, headArg) {
|
|
|
849
881
|
catch (e) {
|
|
850
882
|
return fail(`receipt for ${short} is malformed (${e.message})`);
|
|
851
883
|
}
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
return fail(`receipt for ${short} is malformed`);
|
|
856
|
-
// 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);
|
|
857
887
|
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
|
-
if (badJudge)
|
|
861
|
-
return fail(`receipt for ${short} has a malformed judge block`);
|
|
862
888
|
if (rec.head !== commit)
|
|
863
889
|
return fail(`receipt records head ${String(rec.head).slice(0, 12)}, not ${short}`);
|
|
864
890
|
const now = head(repo);
|
|
@@ -923,7 +949,7 @@ function recordReceipt(repo, cfg, r) {
|
|
|
923
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 };
|
|
924
950
|
const path = writeReceipt(cfg, rec);
|
|
925
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) };
|
|
926
|
-
return { code: PASS, result: { ...result, ...(r.judge && { judge: r.judge }), help: [`Verify this receipt (
|
|
952
|
+
return { code: PASS, result: { ...result, ...(r.judge && { judge: r.judge }), help: [`Verify this receipt (merge gate): done-done receipt --verify ${r.head.commit}`] } };
|
|
927
953
|
}
|
|
928
954
|
// Why evidence from `from` no longer proves `to`, or '' if it still does.
|
|
929
955
|
function staleness(repo, cfg, from, to) {
|
|
@@ -949,10 +975,10 @@ function checkHelp(code, findings, files, { cfg, repo, claim, source, citationBa
|
|
|
949
975
|
if (judgeLevel && code === INCONCLUSIVE)
|
|
950
976
|
return ['Look at the capture yourself, or capture a clearer screen of the outcome and re-run this check'];
|
|
951
977
|
if (code === PASS) {
|
|
952
|
-
// The
|
|
978
|
+
// The merge gate needs a PR on a remote; without one, a receipt records the pass locally.
|
|
953
979
|
const citationArgs = citationBase === undefined ? '' : ` --citation-base ${arg(citationBase)}`;
|
|
954
980
|
if (spawnSync('git', ['remote', 'get-url', 'origin'], { cwd: repo }).status === 0)
|
|
955
|
-
return [`Gate
|
|
981
|
+
return [`Gate the merge on this proof: done-done check --from <PR URL> --trusted-ref origin/main${citationArgs}`];
|
|
956
982
|
const claimArgs = source ? ['--from', source] : ['--outcome', claim ?? '', ...files];
|
|
957
983
|
return [`Record this pass for HEAD: done-done receipt ${claimArgs.map(arg).join(' ')}${citationArgs}`];
|
|
958
984
|
}
|
|
@@ -1880,15 +1906,29 @@ function showRun(dir, cfg, command, gone = false) {
|
|
|
1880
1906
|
: /^locate: no verify skill/.test(run.reason ?? '') ? ['Create the verify skill: done-done init']
|
|
1881
1907
|
: /^locate:/.test(run.reason ?? '') ? [`Make ${cfg.skill} a real folder inside the repository (not a link), commit, then run again: ${again}`]
|
|
1882
1908
|
: [`Read the step logs in ${shown(run.repo, join(dir, 'logs'))}`, `Fix the cause, then run again: ${again}`])
|
|
1883
|
-
: code === PASS ? [`Verify this receipt (
|
|
1909
|
+
: code === PASS ? [`Verify this receipt (merge gate): done-done receipt --verify ${rec.commit}`]
|
|
1884
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`]
|
|
1885
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}`];
|
|
1886
1912
|
emit(result);
|
|
1887
1913
|
return code;
|
|
1888
1914
|
}
|
|
1915
|
+
// A progress line on stderr every 60s (DONE_DONE_HEARTBEAT_MS overrides) naming the running step, so a caller that kills silent commands does not.
|
|
1916
|
+
function heartbeat(dir) {
|
|
1917
|
+
const t = setInterval(() => {
|
|
1918
|
+
const run = loadRun(dir);
|
|
1919
|
+
process.stderr.write(`done-done: run ${run.run_id} still ${run.status}, step ${run.steps.find((s) => s.status === 'running')?.name ?? 'between steps'}\n`);
|
|
1920
|
+
}, Number(process.env.DONE_DONE_HEARTBEAT_MS) || 60_000);
|
|
1921
|
+
return () => clearInterval(t);
|
|
1922
|
+
}
|
|
1889
1923
|
async function waitRun(dir, pid) {
|
|
1890
|
-
|
|
1891
|
-
|
|
1924
|
+
const stop = heartbeat(dir);
|
|
1925
|
+
try {
|
|
1926
|
+
while (['pending', 'running'].includes(loadRun(dir).status) && alive(pid))
|
|
1927
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
1928
|
+
}
|
|
1929
|
+
finally {
|
|
1930
|
+
stop();
|
|
1931
|
+
}
|
|
1892
1932
|
}
|
|
1893
1933
|
// The judge a command will start: the user's agent and judge, asked for when unset.
|
|
1894
1934
|
async function preflightJudge(cfg, needsAgent = true) {
|
|
@@ -1972,7 +2012,13 @@ async function cmdVerify(argv) {
|
|
|
1972
2012
|
// runner goes on when this command or its shell ends; --wait (the default) holds this command until the run ends.
|
|
1973
2013
|
async function launch(dir, cfg, command, how) {
|
|
1974
2014
|
if (how.foreground) {
|
|
1975
|
-
|
|
2015
|
+
const stop = heartbeat(dir);
|
|
2016
|
+
try {
|
|
2017
|
+
await runSteps(dir);
|
|
2018
|
+
}
|
|
2019
|
+
finally {
|
|
2020
|
+
stop();
|
|
2021
|
+
}
|
|
1976
2022
|
return showRun(dir, cfg, command);
|
|
1977
2023
|
}
|
|
1978
2024
|
const fd = openSync(join(dir, 'logs', 'runner.log'), 'a');
|
|
@@ -3446,6 +3492,77 @@ async function cmdLook(argv) {
|
|
|
3446
3492
|
});
|
|
3447
3493
|
return hits.length ? FAIL : PASS;
|
|
3448
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
|
+
}
|
|
3558
|
+
function cmdLint(argv) {
|
|
3559
|
+
const { positionals } = parseArgs({ args: argv, allowPositionals: true });
|
|
3560
|
+
if (positionals.length !== 2)
|
|
3561
|
+
throw new UsageError('lint needs a PR URL or report file, then the evidence folder', 'Re-run as: done-done lint <PR URL | report file> <evidence folder>');
|
|
3562
|
+
const v = lint(positionals[0], positionals[1], home());
|
|
3563
|
+
console.log(v.ok ? 'PASS' : `FAIL: ${v.why}`);
|
|
3564
|
+
return v.ok ? PASS : FAIL;
|
|
3565
|
+
}
|
|
3449
3566
|
async function cmdDesign(argv) {
|
|
3450
3567
|
const { values, positionals } = parseArgs({ args: argv, options: { mock: { type: 'string', multiple: true } }, allowPositionals: true });
|
|
3451
3568
|
const mocks = new Map();
|
|
@@ -3569,6 +3686,10 @@ async function main(argv) {
|
|
|
3569
3686
|
return await cmdDesign(rest);
|
|
3570
3687
|
if (cmd === 'look')
|
|
3571
3688
|
return await cmdLook(rest);
|
|
3689
|
+
if (cmd === 'lint')
|
|
3690
|
+
return cmdLint(rest);
|
|
3691
|
+
if (cmd === 'gate')
|
|
3692
|
+
return cmdGate(rest);
|
|
3572
3693
|
if (cmd === 'status')
|
|
3573
3694
|
return cmdStatus(rest);
|
|
3574
3695
|
if (cmd === 'attach')
|
package/dist/lint.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// `done-done lint`: the deterministic merge gate on a worker's claim. It reads the newest "## Checks" block of a PR body
|
|
2
|
+
// or report file and fails an unproven, reused or oversized claim. No judge, no model, no repo config.
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
6
|
+
import { basename, dirname, isAbsolute, join, resolve } from 'node:path';
|
|
7
|
+
// Third-party, legal text and committed build output are not reviewable code.
|
|
8
|
+
const GENERATED = /(^|\/)(package-lock\.json|pnpm-lock\.yaml|yarn\.lock|Cargo\.lock|go\.sum|[^/]*\.lock)$|(^|\/)vendor\/|(^|\/)dist\/|(^|\/)(LICENSE|LICENSES|NOTICE|COPYING)[^/]*$/;
|
|
9
|
+
export const SIZE_LIMIT = 1000;
|
|
10
|
+
const FIX_TITLE = /^(fix|bugfix|hotfix)\b/i;
|
|
11
|
+
const NOT_APPLICABLE = /^n\/?a\b/i;
|
|
12
|
+
const gh = (...args) => {
|
|
13
|
+
const r = spawnSync('gh', args, { encoding: 'utf8', timeout: 60_000, maxBuffer: 1 << 28 });
|
|
14
|
+
return { status: r.error ? 1 : r.status, stdout: r.stdout ?? '', stderr: r.error?.message ?? r.stderr ?? '' };
|
|
15
|
+
};
|
|
16
|
+
// Added minus deleted non-generated lines of a unified diff: a line moved between files cancels out.
|
|
17
|
+
function netLines(diff) {
|
|
18
|
+
const net = new Map();
|
|
19
|
+
let generated = false;
|
|
20
|
+
for (const l of diff.split('\n')) {
|
|
21
|
+
if (l.startsWith('+++ ')) {
|
|
22
|
+
generated = GENERATED.test(l.startsWith('+++ b/') ? l.slice(6) : l.slice(4));
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (l.startsWith('--- ') || generated || !l.slice(1).trim())
|
|
26
|
+
continue;
|
|
27
|
+
if (l[0] === '+' || l[0] === '-')
|
|
28
|
+
net.set(l.slice(1).trim(), (net.get(l.slice(1).trim()) ?? 0) + (l[0] === '+' ? 1 : -1));
|
|
29
|
+
}
|
|
30
|
+
let total = 0;
|
|
31
|
+
for (const n of net.values())
|
|
32
|
+
if (n > 0)
|
|
33
|
+
total += n;
|
|
34
|
+
return total;
|
|
35
|
+
}
|
|
36
|
+
const sha256 = (p) => createHash('sha256').update(readFileSync(p)).digest('hex');
|
|
37
|
+
// src: a PR URL or a report file; ev: the folder the evidence names are relative to; dir: where the hash store and log live.
|
|
38
|
+
export function lint(src, ev, dir, run = gh) {
|
|
39
|
+
const log = (v) => {
|
|
40
|
+
mkdirSync(dir, { recursive: true });
|
|
41
|
+
appendFileSync(join(dir, 'lint.log'), `${new Date().toISOString()}\t${src}\t${v.head}\t${v.ok ? 'pass' : 'fail'}\t${v.why}\n`);
|
|
42
|
+
return v;
|
|
43
|
+
};
|
|
44
|
+
let body = '', title = '', head = '-', task = '';
|
|
45
|
+
if (existsSync(src) && statSync(src).isFile()) {
|
|
46
|
+
body = readFileSync(src, 'utf8');
|
|
47
|
+
task = basename(dirname(resolve(src)));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const r = run('pr', 'view', src, '--json', 'author,body,title,files,headRefOid,headRefName');
|
|
51
|
+
if (r.status)
|
|
52
|
+
return log({ ok: false, why: `gh pr view failed: ${r.stderr.trim().split('\n').pop()?.slice(0, 160) || '?'}`, head });
|
|
53
|
+
const pr = JSON.parse(r.stdout);
|
|
54
|
+
({ body, title } = { body: pr.body ?? '', title: pr.title ?? '' });
|
|
55
|
+
head = pr.headRefOid ?? '-';
|
|
56
|
+
task = String(pr.headRefName ?? '').split('/').pop();
|
|
57
|
+
const author = pr.author?.login ?? '';
|
|
58
|
+
if (/^(app\/)?(dependabot|renovate)(\[bot\])?$/.test(author))
|
|
59
|
+
return log({ ok: true, why: 'bot', head });
|
|
60
|
+
let size = (pr.files ?? []).reduce((n, f) => n + (GENERATED.test(f.path ?? '') ? 0 : f.additions ?? 0), 0);
|
|
61
|
+
if (size >= SIZE_LIMIT) {
|
|
62
|
+
const d = run('pr', 'diff', src);
|
|
63
|
+
if (!d.status)
|
|
64
|
+
size = netLines(d.stdout);
|
|
65
|
+
}
|
|
66
|
+
if (size >= SIZE_LIMIT)
|
|
67
|
+
return log({ ok: false, why: `${size} added non-generated lines (moved lines excluded): split it (limit ${SIZE_LIMIT})`, head });
|
|
68
|
+
}
|
|
69
|
+
const fail = (why) => log({ ok: false, why, head });
|
|
70
|
+
const block = [...body.matchAll(/^##\s*Checks\s*$([\s\S]*?)(?=^##\s|(?![\s\S]))/gm)].pop(); // newest: workers append, never replace
|
|
71
|
+
if (!block)
|
|
72
|
+
return fail('no "## Checks" block');
|
|
73
|
+
const f = {};
|
|
74
|
+
for (const m of block[1].replace(/```\w*/g, '').matchAll(/^\s*(outcome|repro|proof|evidence)\s*:\s*(.*)$/gm))
|
|
75
|
+
f[m[1]] = m[2].trim();
|
|
76
|
+
const missing = ['outcome', 'repro', 'proof', 'evidence'].filter((k) => !f[k]);
|
|
77
|
+
if (missing.length)
|
|
78
|
+
return fail(`missing or empty Checks line: ${missing.join(', ')}`);
|
|
79
|
+
if (!/[/`]|\.\w{2,4}\b|\b(npm|pnpm|yarn|npx|bash|sh|python3?|node|cargo|go|gradle|make|adb|xcrun|done-done)\b/.test(f.proof))
|
|
80
|
+
return fail('proof: names no command or path');
|
|
81
|
+
const skills = join(process.cwd(), '.agents', 'skills');
|
|
82
|
+
const hasVerify = existsSync(skills) && readdirSync(skills).some((d) => d.startsWith('verify-'));
|
|
83
|
+
if (!f.proof.includes('verify-') && (hasVerify || !/no verify skill/i.test(f.proof)))
|
|
84
|
+
return fail(`proof: does not name the verify-<app> skill run${hasVerify ? '' : ' (or "no verify skill")'}`);
|
|
85
|
+
if (NOT_APPLICABLE.test(f.repro) && FIX_TITLE.test(title))
|
|
86
|
+
return fail('repro: is n/a on a fix PR');
|
|
87
|
+
if (/^not proven\s*:\s*\S/i.test(f.evidence))
|
|
88
|
+
return log({ ok: true, why: `not-proven: ${f.evidence.slice(0, 120)}`, head });
|
|
89
|
+
const names = f.evidence.split(/[,\s]+/).map((n) => n.replace(/^[`"']+|[`"']+$/g, '')).filter((n) => /\.\w{1,10}$/.test(n));
|
|
90
|
+
if (!names.length)
|
|
91
|
+
return fail('evidence: names no files (or write "not proven: <cause>")');
|
|
92
|
+
const store = join(dir, 'lint.hashes');
|
|
93
|
+
const seen = new Map();
|
|
94
|
+
if (existsSync(store))
|
|
95
|
+
for (const l of readFileSync(store, 'utf8').split('\n')) {
|
|
96
|
+
const p = l.split(' ');
|
|
97
|
+
if (p.length === 2)
|
|
98
|
+
seen.set(p[0], p[1]);
|
|
99
|
+
}
|
|
100
|
+
// The evidence folder is born when the task starts, so a file older than it was captured for something else.
|
|
101
|
+
const born = existsSync(ev) ? statSync(ev).birthtimeMs : 0;
|
|
102
|
+
const mine = [];
|
|
103
|
+
let fresh = false;
|
|
104
|
+
for (const n of names) {
|
|
105
|
+
const p = isAbsolute(n) ? n : join(ev, n);
|
|
106
|
+
if (!existsSync(p) || !statSync(p).isFile() || statSync(p).size === 0)
|
|
107
|
+
return fail(`evidence file missing or empty: ${p}`);
|
|
108
|
+
if (born && statSync(p).mtimeMs < born)
|
|
109
|
+
return fail(`evidence file older than the task start: ${p}`);
|
|
110
|
+
const h = sha256(p);
|
|
111
|
+
mine.push(h);
|
|
112
|
+
const owner = seen.get(h); // the same task's earlier lint is not reuse
|
|
113
|
+
if (owner === undefined || owner === src || owner === task)
|
|
114
|
+
fresh = true;
|
|
115
|
+
}
|
|
116
|
+
if (!fresh)
|
|
117
|
+
return fail('every evidence file was already used by another PR');
|
|
118
|
+
if (new Set(mine).size < mine.length)
|
|
119
|
+
return fail('two evidence files are byte-identical: capture each claimed state separately, or write "not proven: <cause>"');
|
|
120
|
+
mkdirSync(dir, { recursive: true });
|
|
121
|
+
for (const h of mine)
|
|
122
|
+
if (!seen.has(h))
|
|
123
|
+
appendFileSync(store, `${h} ${task || src}\n`);
|
|
124
|
+
return log({ ok: true, why: `${names.length} evidence files`, head });
|
|
125
|
+
}
|
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
|
|
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>
|
package/docs/media/hero-dark.svg
CHANGED
|
@@ -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">
|
|
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">
|
|
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">
|
|
145
|
-
<g class="l30"><text xml:space="preserve" x="22" y="792" textLength="460.8" lengthAdjust="spacing"> evidence is for commit
|
|
146
|
-
<g class="l31"><text xml:space="preserve" x="22" y="816" textLength="422.4" lengthAdjust="spacing">
|
|
147
|
-
<g class="l32"><text xml:space="preserve" x="22" y="840" textLength="364.8" lengthAdjust="spacing">
|
|
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>
|
|
156
|
-
<g class="l41"><text xml:space="preserve" x="22" y="168" textLength="460.8" lengthAdjust="spacing"><tspan class="d">commit: </tspan>
|
|
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/
|
|
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">
|
|
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 \"<what you proved>\" ~/</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">
|
|
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">
|
|
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">
|
|
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">
|
|
199
|
-
<g class="l84"><text xml:space="preserve" x="22" y="216" textLength="460.8" lengthAdjust="spacing"> evidence is for commit
|
|
200
|
-
<g class="l85"><text xml:space="preserve" x="22" y="240" textLength="422.4" lengthAdjust="spacing">
|
|
201
|
-
<g class="l86"><text xml:space="preserve" x="22" y="264" textLength="364.8" lengthAdjust="spacing">
|
|
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">
|
|
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">
|
|
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">
|
|
145
|
-
<g class="l30"><text xml:space="preserve" x="22" y="792" textLength="460.8" lengthAdjust="spacing"> evidence is for commit
|
|
146
|
-
<g class="l31"><text xml:space="preserve" x="22" y="816" textLength="422.4" lengthAdjust="spacing">
|
|
147
|
-
<g class="l32"><text xml:space="preserve" x="22" y="840" textLength="364.8" lengthAdjust="spacing">
|
|
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>
|
|
156
|
-
<g class="l41"><text xml:space="preserve" x="22" y="168" textLength="460.8" lengthAdjust="spacing"><tspan class="d">commit: </tspan>
|
|
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/
|
|
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">
|
|
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 \"<what you proved>\" ~/</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">
|
|
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">
|
|
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">
|
|
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">
|
|
199
|
-
<g class="l84"><text xml:space="preserve" x="22" y="216" textLength="460.8" lengthAdjust="spacing"> evidence is for commit
|
|
200
|
-
<g class="l85"><text xml:space="preserve" x="22" y="240" textLength="422.4" lengthAdjust="spacing">
|
|
201
|
-
<g class="l86"><text xml:space="preserve" x="22" y="264" textLength="364.8" lengthAdjust="spacing">
|
|
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>
|
package/docs/media/pass-dark.svg
CHANGED
|
@@ -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">
|
|
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">
|
|
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">
|
|
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">
|
|
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">
|
|
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">
|
|
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">
|
|
18
|
-
<text xml:space="preserve" x="22" y="192" textLength="460.8" lengthAdjust="spacing"> evidence is for commit
|
|
19
|
-
<text xml:space="preserve" x="22" y="216" textLength="422.4" lengthAdjust="spacing">
|
|
20
|
-
<text xml:space="preserve" x="22" y="240" textLength="364.8" lengthAdjust="spacing">
|
|
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">
|
|
18
|
-
<text xml:space="preserve" x="22" y="192" textLength="460.8" lengthAdjust="spacing"> evidence is for commit
|
|
19
|
-
<text xml:space="preserve" x="22" y="216" textLength="422.4" lengthAdjust="spacing">
|
|
20
|
-
<text xml:space="preserve" x="22" y="240" textLength="364.8" lengthAdjust="spacing">
|
|
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>
|
package/docs/media/run-dark.svg
CHANGED
|
@@ -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>
|
|
17
|
-
<text xml:space="preserve" x="22" y="168" textLength="460.8" lengthAdjust="spacing"><tspan class="d">commit: </tspan>
|
|
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/
|
|
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">
|
|
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 \"<what you proved>\" ~/</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">
|
|
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/media/run-light.svg
CHANGED
|
@@ -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>
|
|
17
|
-
<text xml:space="preserve" x="22" y="168" textLength="460.8" lengthAdjust="spacing"><tspan class="d">commit: </tspan>
|
|
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/
|
|
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">
|
|
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 \"<what you proved>\" ~/</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">
|
|
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
|
|
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 |
|
|
@@ -221,6 +221,17 @@ journeys:
|
|
|
221
221
|
| a hit of any kind | 1 |
|
|
222
222
|
| bad flags, no Chrome, or the page did not load | 2 |
|
|
223
223
|
|
|
224
|
+
### Lint check
|
|
225
|
+
|
|
226
|
+
`done-done lint <PR URL | report file> <evidence folder>` is a deterministic gate on a worker's claim: no judge, no model, no config. It prints `PASS` or `FAIL: <reason>` and exits 0, 1, or 2 on a usage error. A PR URL is read with `gh pr view`; a file is read as the report.
|
|
227
|
+
|
|
228
|
+
- The newest `## Checks` block needs `outcome:`, `repro:`, `proof:` and `evidence:`.
|
|
229
|
+
- `proof:` names a command or path, and the repo's `verify-<app>` skill; it may say `no verify skill` only when `.agents/skills/` in the current folder holds none.
|
|
230
|
+
- `repro:` is not `n/a` when the PR title starts with `fix`, `bugfix` or `hotfix`.
|
|
231
|
+
- `evidence: not proven: <cause>` passes.
|
|
232
|
+
- Otherwise each named file (relative to the evidence folder, or absolute) exists, is non-empty and is newer than the evidence folder itself; no two are byte-identical; and at least one has a hash no other PR used. Hashes are kept in `lint.hashes` and each run is appended to `lint.log`, both in `~/.done-done` (`$DONE_DONE_HOME`). A later lint of the same PR is not reuse.
|
|
233
|
+
- A PR with 1,000 or more added non-generated lines fails. Lockfiles, `vendor/`, `dist/` and `LICENSE`-type files do not count, and neither do deletions or lines moved between files. A dependabot or renovate PR passes.
|
|
234
|
+
|
|
224
235
|
### Base-ref before
|
|
225
236
|
|
|
226
237
|
A before/after change is judged as a pair. With `base_ref` set to a branch, tag or commit (for example `base_ref: origin/main`), `verify` drives the base ref too, after the head's drive and only when it passed:
|
|
@@ -240,7 +251,7 @@ done-done check --from pr-body.md # a file, `-` for stdin, or a
|
|
|
240
251
|
|
|
241
252
|
`check` looks only at the files the claim names. It never scans the whole folder.
|
|
242
253
|
|
|
243
|
-
`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
|
|
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.
|
|
244
255
|
|
|
245
256
|
With `--from`, it reads the newest `## Checks` block:
|
|
246
257
|
|
|
@@ -343,7 +354,7 @@ done-done design shots/home.png --mock shots/home.png=design/home.png
|
|
|
343
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.
|
|
344
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.
|
|
345
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`.
|
|
346
|
-
- **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" } }`.
|
|
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" } }`.
|
|
347
358
|
|
|
348
359
|
| Verdict | Result | Exit |
|
|
349
360
|
|---|---|---|
|
|
@@ -356,7 +367,7 @@ The result lists each screen (failing first, then unsure, then passing; `--full`
|
|
|
356
367
|
|
|
357
368
|
### Record a receipt
|
|
358
369
|
|
|
359
|
-
A receipt ties one passing check to one commit and to the exact bytes it cited, so a merge
|
|
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.
|
|
360
371
|
|
|
361
372
|
```sh
|
|
362
373
|
done-done receipt --from pr-body.md # or --outcome "<claim>" <files...>, or --from -
|
|
@@ -370,7 +381,7 @@ done-done receipt --verify <head> # read-only: check a recorded receipt
|
|
|
370
381
|
### Verify a change in one run
|
|
371
382
|
|
|
372
383
|
```sh
|
|
373
|
-
done-done verify [<journey>] --outcome "<what a user can now do>" # --no-wait, --wait (the default), or --foreground (
|
|
384
|
+
done-done verify [<journey>] --outcome "<what a user can now do>" # --no-wait, --wait (the default), or --foreground (for scripts)
|
|
374
385
|
done-done verify --outcome "<claim>" [--feature <name>] # no journey: the author agent drives a verify skill feature
|
|
375
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)
|
|
376
387
|
done-done status [--run <run-id>] # the newest run of this checkout by default
|
|
@@ -434,7 +445,7 @@ npx -y sign-off-axi post --kind <gate|evidence> --from <file> # the command si
|
|
|
434
445
|
### Keep the verify skill true
|
|
435
446
|
|
|
436
447
|
```sh
|
|
437
|
-
done-done maintain # --no-wait, --wait (the default), or --foreground (
|
|
448
|
+
done-done maintain # --no-wait, --wait (the default), or --foreground (for scripts)
|
|
438
449
|
```
|
|
439
450
|
|
|
440
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:
|
|
@@ -471,7 +482,7 @@ Running `done-done` with no subcommand prints the repo's config state (`config:
|
|
|
471
482
|
|
|
472
483
|
`doctor` exits 2 when a required item (`git`, `config`, `evidence_root`, `agent`, `judge`, and `probe` with `--probe`) is missing.
|
|
473
484
|
|
|
474
|
-
###
|
|
485
|
+
### Merge gate on the desktop
|
|
475
486
|
|
|
476
487
|
```sh
|
|
477
488
|
PR_BRANCH="$(gh pr view "$PR_URL" --json headRefName --jq .headRefName)"
|
|
@@ -480,14 +491,35 @@ done-done receipt --from - --trusted-ref origin/main --claim-id "$PR_BRANCH" < p
|
|
|
480
491
|
done-done receipt --verify "$HEAD" # re-verify later
|
|
481
492
|
```
|
|
482
493
|
|
|
483
|
-
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
|
|
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.
|
|
484
517
|
|
|
485
518
|
### Where proof lives
|
|
486
519
|
|
|
487
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.
|
|
488
521
|
|
|
489
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.
|
|
490
|
-
- **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>`.
|
|
491
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.
|
|
492
524
|
|
|
493
525
|
## Config
|
package/package.json
CHANGED
|
@@ -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`.
|
|
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
|
|
@@ -46,6 +48,18 @@ journeys:
|
|
|
46
48
|
command: done-done look http://127.0.0.1:3000/inbox --out "$DONE_DONE_EVIDENCE_ROOT/inbox.png"
|
|
47
49
|
```
|
|
48
50
|
|
|
51
|
+
To gate a worker's claim cheaply, `done-done lint <PR URL | report file> <evidence folder>` reads the newest `## Checks` block
|
|
52
|
+
with no judge or model and prints `PASS` or `FAIL: <reason>` (exit 1 on a fail): missing lines, evidence that is absent,
|
|
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.
|
|
54
|
+
`evidence: not proven: <cause>` passes. Fix the cause it names and re-run; never edit the evidence to get past it.
|
|
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
|
+
|
|
49
63
|
done-done starts a coding agent the user chose. When it says no agent is set, ask the user which agent
|
|
50
64
|
(and model) to use and save their answer with the command it prints; never choose one for them.
|
|
51
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>
|