detached 31c9416

docs(mr): notes, threads, ranges and verdicts

commit
31c94166ae5ebf5087deef788083ff29c29977c6
author
brice · · 2026-10-19 05:50 UTC
parent
f8d5320
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
5 files · +58 -14
M docs/cli.md +2 -1
@@ -55,7 +55,8 @@ ermite token create <label> [--preset cli|agent|ci|runner] [--scope …] [--repo
5555 ermite token list | revoke <id>
5656
5757 ermite mr create <repo> --source <branch> --title … | list <repo> | show <repo> <n> [--diff]
58-ermite mr comment <repo> <n> -m … [--path … --line … --side old|new] | resolve <repo> <n> <comment>
58+ermite mr comment <repo> <n> -m … [--path … --line N|A-B --side old|new] [--reply <id>] [--approve|--request-changes]
59+ermite mr resolve <repo> <n> <comment> | edit-comment <repo> <n> <comment> -m … | delete-comment <repo> <n> <comment>
5960 ermite mr merge <repo> <n> --head <id> [--squash] [--delete-branch] | close <repo> <n>
6061
6162 ermite run list <repo> [--workflow …] [--branch …] [--status …]
@@ -55,7 +55,8 @@ ermite token create <label> [--preset cli|agent|ci|runner] [--scope …] [--repo
55 ermite token list | revoke <id>55 ermite token list | revoke <id>
56 56
57 ermite mr create <repo> --source <branch> --title … | list <repo> | show <repo> <n> [--diff]57 ermite mr create <repo> --source <branch> --title … | list <repo> | show <repo> <n> [--diff]
58-ermite mr comment <repo> <n> -m … [--path … --line … --side old|new] | resolve <repo> <n> <comment>58+ermite mr comment <repo> <n> -m … [--path … --line N|A-B --side old|new] [--reply <id>] [--approve|--request-changes]
59+ermite mr resolve <repo> <n> <comment> | edit-comment <repo> <n> <comment> -m … | delete-comment <repo> <n> <comment>
59 ermite mr merge <repo> <n> --head <id> [--squash] [--delete-branch] | close <repo> <n>60 ermite mr merge <repo> <n> --head <id> [--squash] [--delete-branch] | close <repo> <n>
60 61
61 ermite run list <repo> [--workflow …] [--branch …] [--status …]62 ermite run list <repo> [--workflow …] [--branch …] [--status …]
M docs/merge-requests.md +30 -1
@@ -13,10 +13,39 @@ ermite mr merge myrepo 1 --head <commit> [--squash] [--delete-branch]
1313 - With `failing_checks_block` on (default), the merge is refused while a check of the head failed or is still running.
1414 - `--delete-branch` deletes the source branch, unless it is the default branch or another open request uses it.
1515 - `ermite repo edit --merge-modes` limits the modes per repository.
16-- Scopes: `mr:write` to open and comment, `mr:merge` to merge.
16+- Scopes: `mr:write` to open and comment, `mr:review` to give a verdict, `mr:merge` to merge.
1717
1818 Verify squash commits with the `allowed_signers` line from `ermite signing-key show`:
1919
2020 ```sh
2121 git verify-commit <commit>
2222 ```
23+
24+## Notes
25+
26+A note is the only review object. It is published as soon as it is written: there is no draft and no submit step.
27+
28+```sh
29+ermite mr comment myrepo 1 -m "General remark"
30+ermite mr comment myrepo 1 -m "Why?" --path main.go --line 10-14 --side new
31+ermite mr comment myrepo 1 -m "Fixed in ^3" --reply 2
32+ermite mr resolve myrepo 1 2
33+ermite mr edit-comment myrepo 1 2 -m "New text"
34+ermite mr delete-comment myrepo 1 2
35+```
36+
37+- Notes are Markdown, up to 8000 characters, rendered without raw HTML. Quote with `> `.
38+- `^N` in a note or a description links to note N of the same merge request. A reference to a missing note stays plain text.
39+- A note is general, on one line, or on a range `A-B` of lines on one side (`old` or `new`) of the diff. The lines must be in the diff.
40+- Threads have one level: a reply (`--reply <id>`) takes the anchor of its root and cannot carry a verdict. Resolving or reopening is done on the root and covers its replies.
41+- Only the author edits or deletes a note. An edited note shows "edited". A deleted note loses its text; a deleted root with replies stays as "deleted note".
42+
43+## Verdicts
44+
45+A root note can carry a verdict: `--approve` or `--request-changes`. The text may then be empty. A verdict needs the `mr:review` scope (or the owner); `mr:write` comments but cannot give one.
46+
47+- A verdict ends a review: agents and `ermite watch` act on it, not on each note.
48+- Verdicts are informative: none blocks or allows a merge.
49+- `mr show` lists the latest verdict of each author (`verdicts` in `--json`).
50+- An approval is stale once the branch head differs from the head it was given on. Changes requested stay current until their author gives a new verdict, whatever is pushed.
51+- Verdicts are immutable: editing or deleting the note keeps its verdict. Only a new verdict of the same author replaces it.
@@ -13,10 +13,39 @@ ermite mr merge myrepo 1 --head <commit> [--squash] [--delete-branch]
13 - With `failing_checks_block` on (default), the merge is refused while a check of the head failed or is still running.13 - With `failing_checks_block` on (default), the merge is refused while a check of the head failed or is still running.
14 - `--delete-branch` deletes the source branch, unless it is the default branch or another open request uses it.14 - `--delete-branch` deletes the source branch, unless it is the default branch or another open request uses it.
15 - `ermite repo edit --merge-modes` limits the modes per repository.15 - `ermite repo edit --merge-modes` limits the modes per repository.
16-- Scopes: `mr:write` to open and comment, `mr:merge` to merge.16+- Scopes: `mr:write` to open and comment, `mr:review` to give a verdict, `mr:merge` to merge.
17 17
18 Verify squash commits with the `allowed_signers` line from `ermite signing-key show`:18 Verify squash commits with the `allowed_signers` line from `ermite signing-key show`:
19 19
20 ```sh20 ```sh
21 git verify-commit <commit>21 git verify-commit <commit>
22 ```22 ```
23+
24+## Notes
25+
26+A note is the only review object. It is published as soon as it is written: there is no draft and no submit step.
27+
28+```sh
29+ermite mr comment myrepo 1 -m "General remark"
30+ermite mr comment myrepo 1 -m "Why?" --path main.go --line 10-14 --side new
31+ermite mr comment myrepo 1 -m "Fixed in ^3" --reply 2
32+ermite mr resolve myrepo 1 2
33+ermite mr edit-comment myrepo 1 2 -m "New text"
34+ermite mr delete-comment myrepo 1 2
35+```
36+
37+- Notes are Markdown, up to 8000 characters, rendered without raw HTML. Quote with `> `.
38+- `^N` in a note or a description links to note N of the same merge request. A reference to a missing note stays plain text.
39+- A note is general, on one line, or on a range `A-B` of lines on one side (`old` or `new`) of the diff. The lines must be in the diff.
40+- Threads have one level: a reply (`--reply <id>`) takes the anchor of its root and cannot carry a verdict. Resolving or reopening is done on the root and covers its replies.
41+- Only the author edits or deletes a note. An edited note shows "edited". A deleted note loses its text; a deleted root with replies stays as "deleted note".
42+
43+## Verdicts
44+
45+A root note can carry a verdict: `--approve` or `--request-changes`. The text may then be empty. A verdict needs the `mr:review` scope (or the owner); `mr:write` comments but cannot give one.
46+
47+- A verdict ends a review: agents and `ermite watch` act on it, not on each note.
48+- Verdicts are informative: none blocks or allows a merge.
49+- `mr show` lists the latest verdict of each author (`verdicts` in `--json`).
50+- An approval is stale once the branch head differs from the head it was given on. Changes requested stay current until their author gives a new verdict, whatever is pushed.
51+- Verdicts are immutable: editing or deleting the note keeps its verdict. Only a new verdict of the same author replaces it.
M docs/tokens.md +2 -2
@@ -6,7 +6,7 @@ ermite token list
66 ermite token revoke <id>
77 ```
88
9-The secret is printed once. Presets and `--scope` add up. The full scope list is in [`skills/ermite/CLI.md`](../skills/ermite/CLI.md).
9+The secret is printed once. Presets and `--scope` add up. The full scope list is in [`skills/ermite/CLI.md`](../skills/ermite/CLI.md). `mr:review` lets a token give a merge request verdict (`--approve`, `--request-changes`); the `cli` preset includes it, the `agent` preset does not. A token created before `mr:review` existed lacks it: recreate it with `--preset cli` to keep the owner's rights.
1010
1111 | Preset | For |
1212 |---|---|
@@ -28,7 +28,7 @@ ermite token create claude --preset agent --scope repo:read --repo myrepo --bran
2828 The token **can**:
2929
3030 - push, force-push and delete branches under its prefixes;
31-- open, comment on and close merge requests;
31+- open, comment on and close merge requests; with `mr:review`, give verdicts;
3232 - with `mr:merge`, merge into a branch under its prefixes;
3333 - read CI runs, logs and checks; with `ci:write`, cancel and re-run runs of its own branches;
3434 - create tokens whose prefixes lie within its own.
@@ -6,7 +6,7 @@ ermite token list
6 ermite token revoke <id>6 ermite token revoke <id>
7 ```7 ```
8 8
9-The secret is printed once. Presets and `--scope` add up. The full scope list is in [`skills/ermite/CLI.md`](../skills/ermite/CLI.md).9+The secret is printed once. Presets and `--scope` add up. The full scope list is in [`skills/ermite/CLI.md`](../skills/ermite/CLI.md). `mr:review` lets a token give a merge request verdict (`--approve`, `--request-changes`); the `cli` preset includes it, the `agent` preset does not. A token created before `mr:review` existed lacks it: recreate it with `--preset cli` to keep the owner's rights.
10 10
11 | Preset | For |11 | Preset | For |
12 |---|---|12 |---|---|
@@ -28,7 +28,7 @@ ermite token create claude --preset agent --scope repo:read --repo myrepo --bran
28 The token **can**:28 The token **can**:
29 29
30 - push, force-push and delete branches under its prefixes;30 - push, force-push and delete branches under its prefixes;
31-- open, comment on and close merge requests;31+- open, comment on and close merge requests; with `mr:review`, give verdicts;
32 - with `mr:merge`, merge into a branch under its prefixes;32 - with `mr:merge`, merge into a branch under its prefixes;
33 - read CI runs, logs and checks; with `ci:write`, cancel and re-run runs of its own branches;33 - read CI runs, logs and checks; with `ci:write`, cancel and re-run runs of its own branches;
34 - create tokens whose prefixes lie within its own.34 - create tokens whose prefixes lie within its own.
M skills/ermite/CLI.md +11 -6
@@ -28,7 +28,7 @@ Presets add up with `--scope` (repeatable or comma-separated):
2828
2929 | Preset | Scopes |
3030 |---|---|
31-| `cli` | every scope, for the owner's daily use |
31+| `cli` | every scope (including `mr:review`), for the owner's daily use; a token created before `mr:review` existed lacks it and loses owner rights: recreate it with `--preset cli` |
3232 | `agent` | `repo:write`, `mr:write` |
3333 | `ci` | `repo:read` |
3434
@@ -39,7 +39,8 @@ Scopes:
3939 | `repo:read` | read private repositories |
4040 | `repo:write` | push, create a repository |
4141 | `repo:admin` | delete a repository, change visibility, description, default branch, merge settings |
42-| `mr:write` | open, comment on, resolve, close, mark read |
42+| `mr:write` | open, comment on, reply, edit and delete own notes, resolve, close, mark read |
43+| `mr:review` | give a verdict (`--approve`, `--request-changes`) |
4344 | `mr:merge` | merge, and delete the merged source branch with `--delete-branch` |
4445 | `token:admin` | create, list and revoke tokens |
4546 | `ci:write`, `secrets:write`, `runner:register` | reserved for CI |
@@ -55,12 +56,16 @@ A token can never grant more than the token that creates it. `--repo` (repeatabl
5556 | `mr create <repo> --source B --title T [--target B] [--description TEXT\|-]` | `mr:write` | target defaults to the default branch; title of 120 characters max; `-` reads standard input |
5657 | `mr list <repo> [--state open\|merged\|closed\|all] [--json]` | none | default `open` |
5758 | `mr show <repo> <n> [--diff] [--since-last-read] [--mark-read] [--json]` | `mr:write` for `--mark-read` | commits, changed files and comments; `--diff` adds hunks with line numbers |
58-| `mr comment <repo> <n> -m TEXT\|- [--path P --line N --side old\|new]` | `mr:write` | the line must be in the current diff; a deleted file uses its old path |
59-| `mr resolve <repo> <n> <comment-id> [--undo]` | `mr:write` | doing it twice is not an error |
59+| `mr comment <repo> <n> -m TEXT\|- [--path P --line N\|A-B --side old\|new] [--reply ID] [--approve\|--request-changes]` | `mr:write`; verdicts `mr:review` | Markdown note (8000 characters max); the lines must be in the current diff on one side; a deleted file uses its old path; `--reply` takes a root note (`12`, `#12` or `^12`), inherits its anchor and cannot carry a verdict; a verdict note may have no message |
60+| `mr edit-comment <repo> <n> <comment-id> -m TEXT\|-` | `mr:write` | author only, open merge request; the verdict is unchanged |
61+| `mr delete-comment <repo> <n> <comment-id>` | `mr:write` | author only; the text is lost, the verdict is kept |
62+| `mr resolve <repo> <n> <comment-id> [--undo]` | `mr:write` | on the thread root, covers its replies; doing it twice is not an error |
6063 | `mr merge <repo> <n> --head <full-id> [--squash] [--delete-branch]` | `mr:merge` | refused if the branch moved or the target is not an ancestor of the head; `--delete-branch` deletes the source after the merge (refused for the default branch or a branch another open request uses; JSON adds `source_branch_deleted`) |
6164 | `mr close <repo> <n>` | `mr:write` | a closed merge request cannot be reopened |
6265
63-Merge request JSON: `repo`, `number`, `title`, `description`, `source_branch`, `target_branch`, `state`, `author` (token label), `created_at`, `updated_at`, `head_id`, `merged_commit_id`, `merge_mode`. `mr show --json` adds `commits`, `diff_base_id`, `stats`, `files` (with `hunks` under `--diff`: `kind`, `old_number`, `new_number`, `text`), `comments`, `last_read_id`, `last_read_lost`.
66+Merge request JSON: `repo`, `number`, `title`, `description`, `source_branch`, `target_branch`, `state`, `author` (token label), `created_at`, `updated_at`, `head_id`, `merged_commit_id`, `merge_mode`. `mr show --json` adds `commits`, `diff_base_id`, `stats`, `files` (with `hunks` under `--diff`: `kind`, `old_number`, `new_number`, `text`), `comments`, `verdicts`, `last_read_id`, `last_read_lost`.
67+
68+Comment JSON: `id`, `author`, `path`, `line`, `side`, `commit_id`, `text`, `resolved`, `created_at`, `reply_to` (empty for roots), `start_line` (0 for a single line), `verdict` (`""`, `approve`, `changes_requested`), `edited_at`, `deleted`. Verdict JSON (latest per author): `author`, `verdict`, `comment_id`, `commit_id`, `created_at`, `stale` (an approval given on another head; changes requested are never stale). Verdicts are informative and never block a merge. In notes, `^N` refers to note N.
6469
6570 ## Workflows
6671
@@ -82,7 +87,7 @@ Workflow JSON: `repo`, `file` (always `.github/workflows/<file>`), `name` (the `
8287 | `repo_created`, `repo_updated`, `repo_deleted` | `repo`, `repository` |
8388 | `pushed` | `repo`, `ref_updates` (`ref`, `old_id`, `new_id`; an empty id means created or deleted) |
8489 | `merge_request_opened`, `merge_request_merged`, `merge_request_closed` | `repo`, `merge_request` |
85-| `comment_added` | `repo`, `number`, `comment` |
90+| `comment_added`, `comment_edited`, `comment_deleted`, `comment_resolved` | `repo`, `number`, `comment` (a verdict note has `verdict` set) |
8691 | `gap` | none: events were lost, reload the state |
8792
8893 ## Server and web
@@ -28,7 +28,7 @@ Presets add up with `--scope` (repeatable or comma-separated):
28 28
29 | Preset | Scopes |29 | Preset | Scopes |
30 |---|---|30 |---|---|
31-| `cli` | every scope, for the owner's daily use |31+| `cli` | every scope (including `mr:review`), for the owner's daily use; a token created before `mr:review` existed lacks it and loses owner rights: recreate it with `--preset cli` |
32 | `agent` | `repo:write`, `mr:write` |32 | `agent` | `repo:write`, `mr:write` |
33 | `ci` | `repo:read` |33 | `ci` | `repo:read` |
34 34
@@ -39,7 +39,8 @@ Scopes:
39 | `repo:read` | read private repositories |39 | `repo:read` | read private repositories |
40 | `repo:write` | push, create a repository |40 | `repo:write` | push, create a repository |
41 | `repo:admin` | delete a repository, change visibility, description, default branch, merge settings |41 | `repo:admin` | delete a repository, change visibility, description, default branch, merge settings |
42-| `mr:write` | open, comment on, resolve, close, mark read |42+| `mr:write` | open, comment on, reply, edit and delete own notes, resolve, close, mark read |
43+| `mr:review` | give a verdict (`--approve`, `--request-changes`) |
43 | `mr:merge` | merge, and delete the merged source branch with `--delete-branch` |44 | `mr:merge` | merge, and delete the merged source branch with `--delete-branch` |
44 | `token:admin` | create, list and revoke tokens |45 | `token:admin` | create, list and revoke tokens |
45 | `ci:write`, `secrets:write`, `runner:register` | reserved for CI |46 | `ci:write`, `secrets:write`, `runner:register` | reserved for CI |
@@ -55,12 +56,16 @@ A token can never grant more than the token that creates it. `--repo` (repeatabl
55 | `mr create <repo> --source B --title T [--target B] [--description TEXT\|-]` | `mr:write` | target defaults to the default branch; title of 120 characters max; `-` reads standard input |56 | `mr create <repo> --source B --title T [--target B] [--description TEXT\|-]` | `mr:write` | target defaults to the default branch; title of 120 characters max; `-` reads standard input |
56 | `mr list <repo> [--state open\|merged\|closed\|all] [--json]` | none | default `open` |57 | `mr list <repo> [--state open\|merged\|closed\|all] [--json]` | none | default `open` |
57 | `mr show <repo> <n> [--diff] [--since-last-read] [--mark-read] [--json]` | `mr:write` for `--mark-read` | commits, changed files and comments; `--diff` adds hunks with line numbers |58 | `mr show <repo> <n> [--diff] [--since-last-read] [--mark-read] [--json]` | `mr:write` for `--mark-read` | commits, changed files and comments; `--diff` adds hunks with line numbers |
58-| `mr comment <repo> <n> -m TEXT\|- [--path P --line N --side old\|new]` | `mr:write` | the line must be in the current diff; a deleted file uses its old path |59+| `mr comment <repo> <n> -m TEXT\|- [--path P --line N\|A-B --side old\|new] [--reply ID] [--approve\|--request-changes]` | `mr:write`; verdicts `mr:review` | Markdown note (8000 characters max); the lines must be in the current diff on one side; a deleted file uses its old path; `--reply` takes a root note (`12`, `#12` or `^12`), inherits its anchor and cannot carry a verdict; a verdict note may have no message |
59-| `mr resolve <repo> <n> <comment-id> [--undo]` | `mr:write` | doing it twice is not an error |60+| `mr edit-comment <repo> <n> <comment-id> -m TEXT\|-` | `mr:write` | author only, open merge request; the verdict is unchanged |
61+| `mr delete-comment <repo> <n> <comment-id>` | `mr:write` | author only; the text is lost, the verdict is kept |
62+| `mr resolve <repo> <n> <comment-id> [--undo]` | `mr:write` | on the thread root, covers its replies; doing it twice is not an error |
60 | `mr merge <repo> <n> --head <full-id> [--squash] [--delete-branch]` | `mr:merge` | refused if the branch moved or the target is not an ancestor of the head; `--delete-branch` deletes the source after the merge (refused for the default branch or a branch another open request uses; JSON adds `source_branch_deleted`) |63 | `mr merge <repo> <n> --head <full-id> [--squash] [--delete-branch]` | `mr:merge` | refused if the branch moved or the target is not an ancestor of the head; `--delete-branch` deletes the source after the merge (refused for the default branch or a branch another open request uses; JSON adds `source_branch_deleted`) |
61 | `mr close <repo> <n>` | `mr:write` | a closed merge request cannot be reopened |64 | `mr close <repo> <n>` | `mr:write` | a closed merge request cannot be reopened |
62 65
63-Merge request JSON: `repo`, `number`, `title`, `description`, `source_branch`, `target_branch`, `state`, `author` (token label), `created_at`, `updated_at`, `head_id`, `merged_commit_id`, `merge_mode`. `mr show --json` adds `commits`, `diff_base_id`, `stats`, `files` (with `hunks` under `--diff`: `kind`, `old_number`, `new_number`, `text`), `comments`, `last_read_id`, `last_read_lost`.66+Merge request JSON: `repo`, `number`, `title`, `description`, `source_branch`, `target_branch`, `state`, `author` (token label), `created_at`, `updated_at`, `head_id`, `merged_commit_id`, `merge_mode`. `mr show --json` adds `commits`, `diff_base_id`, `stats`, `files` (with `hunks` under `--diff`: `kind`, `old_number`, `new_number`, `text`), `comments`, `verdicts`, `last_read_id`, `last_read_lost`.
67+
68+Comment JSON: `id`, `author`, `path`, `line`, `side`, `commit_id`, `text`, `resolved`, `created_at`, `reply_to` (empty for roots), `start_line` (0 for a single line), `verdict` (`""`, `approve`, `changes_requested`), `edited_at`, `deleted`. Verdict JSON (latest per author): `author`, `verdict`, `comment_id`, `commit_id`, `created_at`, `stale` (an approval given on another head; changes requested are never stale). Verdicts are informative and never block a merge. In notes, `^N` refers to note N.
64 69
65 ## Workflows70 ## Workflows
66 71
@@ -82,7 +87,7 @@ Workflow JSON: `repo`, `file` (always `.github/workflows/<file>`), `name` (the `
82 | `repo_created`, `repo_updated`, `repo_deleted` | `repo`, `repository` |87 | `repo_created`, `repo_updated`, `repo_deleted` | `repo`, `repository` |
83 | `pushed` | `repo`, `ref_updates` (`ref`, `old_id`, `new_id`; an empty id means created or deleted) |88 | `pushed` | `repo`, `ref_updates` (`ref`, `old_id`, `new_id`; an empty id means created or deleted) |
84 | `merge_request_opened`, `merge_request_merged`, `merge_request_closed` | `repo`, `merge_request` |89 | `merge_request_opened`, `merge_request_merged`, `merge_request_closed` | `repo`, `merge_request` |
85-| `comment_added` | `repo`, `number`, `comment` |90+| `comment_added`, `comment_edited`, `comment_deleted`, `comment_resolved` | `repo`, `number`, `comment` (a verdict note has `verdict` set) |
86 | `gap` | none: events were lost, reload the state |91 | `gap` | none: events were lost, reload the state |
87 92
88 ## Server and web93 ## Server and web
M skills/ermite/SKILL.md +13 -4
@@ -35,11 +35,20 @@ git push https://git.example.com/<repo>.git main # password = token,
3535
3636 1. List: `ermite mr list <repo> --json`.
3737 2. Read: `ermite mr show <repo> <n> --diff --json`. Keep `head_id`; take line numbers from the hunks.
38-3. Comment on a line: `ermite mr comment <repo> <n> -m "text" --path <file> --line <n> --side new`. Without `--path` the comment is general. The line must appear in the diff.
39-4. Merge only when asked: `ermite mr merge <repo> <n> --head <head_id>`, with `--squash` for one commit signed by the server and `--delete-branch` to delete the source branch afterwards.
38+3. Comment on a line: `ermite mr comment <repo> <n> -m "text" --path <file> --line <n> --side new`; `--line 10-14` covers a range on one side. Without `--path` the comment is general. The line must appear in the diff. Notes are Markdown; `^N` refers to note N.
39+4. End a review with a verdict (needs `mr:review`): `--approve` or `--request-changes`, alone or with a message. Verdicts are informative and cannot be edited; a new verdict replaces the previous one.
40+5. Merge only when asked: `ermite mr merge <repo> <n> --head <head_id>`, with `--squash` for one commit signed by the server and `--delete-branch` to delete the source branch afterwards.
4041
4142 The merge is refused when the branch moved since `head_id` (review again) or when the target is not an ancestor of the head (rebase and push again). The repository may allow only one merge mode.
4243
44+### Act on a review
45+
46+Notes are visible as soon as they are written, so a review is not finished until its verdict. Act on a verdict, not on each note (`ermite watch --json` shows `verdict` on the `comment_added` event).
47+
48+On `changes_requested`: run `ermite mr show <repo> <n> --json`, read the open threads (roots with `resolved` false and their replies), fix the code, push, then answer each thread with `ermite mr comment <repo> <n> --reply <id> -m "Fixed in ^N"`. Leave resolving to the reviewer: do not run `mr resolve` on their threads. Edit or delete only your own notes (`mr edit-comment`, `mr delete-comment`).
49+
50+An approval is stale after a push; changes requested stay until the reviewer gives a new verdict.
51+
4352 Open one: `ermite mr create <repo> --source <branch> --title "..."`. After new pushes, `mr show --since-last-read` shows only what changed since `--mark-read`.
4453
4554 ## Tokens
@@ -48,11 +57,11 @@ Open one: `ermite mr create <repo> --source <branch> --title "..."`. After new p
4857 ermite token create my-agent --preset agent --repo <repo> --expires 30d
4958 ```
5059
51-The secret is printed once. Presets: `agent` (push, open and comment on merge requests), `ci` (read), `cli` (everything). Add `--scope mr:merge` to let an agent merge. A token can never grant more than the token that creates it. Full scope list: [CLI.md](CLI.md).
60+The secret is printed once. Presets: `agent` (push, open and comment on merge requests; no verdicts), `ci` (read), `cli` (everything). Add `--scope mr:review` to let an agent give verdicts, `--scope mr:merge` to let it merge. A token can never grant more than the token that creates it. Full scope list: [CLI.md](CLI.md).
5261
5362 ## Follow events
5463
55-`ermite watch [repo] --json` prints one JSON object per event (pushes, repositories, merge requests, comments). It reconnects and resumes on its own; a `gap` event means some events were lost, so reload the state.
64+`ermite watch [repo] --json` prints one JSON object per event (pushes, repositories, merge requests, notes: added, edited, deleted, resolved). It reconnects and resumes on its own; a `gap` event means some events were lost, so reload the state.
5665
5766 ## Run a server
5867
@@ -35,11 +35,20 @@ git push https://git.example.com/<repo>.git main # password = token,
35 35
36 1. List: `ermite mr list <repo> --json`.36 1. List: `ermite mr list <repo> --json`.
37 2. Read: `ermite mr show <repo> <n> --diff --json`. Keep `head_id`; take line numbers from the hunks.37 2. Read: `ermite mr show <repo> <n> --diff --json`. Keep `head_id`; take line numbers from the hunks.
38-3. Comment on a line: `ermite mr comment <repo> <n> -m "text" --path <file> --line <n> --side new`. Without `--path` the comment is general. The line must appear in the diff.38+3. Comment on a line: `ermite mr comment <repo> <n> -m "text" --path <file> --line <n> --side new`; `--line 10-14` covers a range on one side. Without `--path` the comment is general. The line must appear in the diff. Notes are Markdown; `^N` refers to note N.
39-4. Merge only when asked: `ermite mr merge <repo> <n> --head <head_id>`, with `--squash` for one commit signed by the server and `--delete-branch` to delete the source branch afterwards.39+4. End a review with a verdict (needs `mr:review`): `--approve` or `--request-changes`, alone or with a message. Verdicts are informative and cannot be edited; a new verdict replaces the previous one.
40+5. Merge only when asked: `ermite mr merge <repo> <n> --head <head_id>`, with `--squash` for one commit signed by the server and `--delete-branch` to delete the source branch afterwards.
40 41
41 The merge is refused when the branch moved since `head_id` (review again) or when the target is not an ancestor of the head (rebase and push again). The repository may allow only one merge mode.42 The merge is refused when the branch moved since `head_id` (review again) or when the target is not an ancestor of the head (rebase and push again). The repository may allow only one merge mode.
42 43
44+### Act on a review
45+
46+Notes are visible as soon as they are written, so a review is not finished until its verdict. Act on a verdict, not on each note (`ermite watch --json` shows `verdict` on the `comment_added` event).
47+
48+On `changes_requested`: run `ermite mr show <repo> <n> --json`, read the open threads (roots with `resolved` false and their replies), fix the code, push, then answer each thread with `ermite mr comment <repo> <n> --reply <id> -m "Fixed in ^N"`. Leave resolving to the reviewer: do not run `mr resolve` on their threads. Edit or delete only your own notes (`mr edit-comment`, `mr delete-comment`).
49+
50+An approval is stale after a push; changes requested stay until the reviewer gives a new verdict.
51+
43 Open one: `ermite mr create <repo> --source <branch> --title "..."`. After new pushes, `mr show --since-last-read` shows only what changed since `--mark-read`.52 Open one: `ermite mr create <repo> --source <branch> --title "..."`. After new pushes, `mr show --since-last-read` shows only what changed since `--mark-read`.
44 53
45 ## Tokens54 ## Tokens
@@ -48,11 +57,11 @@ Open one: `ermite mr create <repo> --source <branch> --title "..."`. After new p
48 ermite token create my-agent --preset agent --repo <repo> --expires 30d57 ermite token create my-agent --preset agent --repo <repo> --expires 30d
49 ```58 ```
50 59
51-The secret is printed once. Presets: `agent` (push, open and comment on merge requests), `ci` (read), `cli` (everything). Add `--scope mr:merge` to let an agent merge. A token can never grant more than the token that creates it. Full scope list: [CLI.md](CLI.md).60+The secret is printed once. Presets: `agent` (push, open and comment on merge requests; no verdicts), `ci` (read), `cli` (everything). Add `--scope mr:review` to let an agent give verdicts, `--scope mr:merge` to let it merge. A token can never grant more than the token that creates it. Full scope list: [CLI.md](CLI.md).
52 61
53 ## Follow events62 ## Follow events
54 63
55-`ermite watch [repo] --json` prints one JSON object per event (pushes, repositories, merge requests, comments). It reconnects and resumes on its own; a `gap` event means some events were lost, so reload the state.64+`ermite watch [repo] --json` prints one JSON object per event (pushes, repositories, merge requests, notes: added, edited, deleted, resolved). It reconnects and resumes on its own; a `gap` event means some events were lost, so reload the state.
56 65
57 ## Run a server66 ## Run a server
58 67

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 43

↑↓ moveenter openesc close