docs(mr): notes, threads, ranges and verdicts
- commit
- 31c94166ae5ebf5087deef788083ff29c29977c6
- author
- brice · · 2026-10-19 05:50 UTC
- parent
- f8d5320
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· 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 | |||
| 55 | 55 | ermite token list | revoke <id> | |
| 56 | 56 | ||
| 57 | 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 | 60 | ermite mr merge <repo> <n> --head <id> [--squash] [--delete-branch] | close <repo> <n> | |
| 60 | 61 | ||
| 61 | 62 | 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] | |||
| 13 | 13 | - With `failing_checks_block` on (default), the merge is refused while a check of the head failed or is still running. | |
| 14 | 14 | - `--delete-branch` deletes the source branch, unless it is the default branch or another open request uses it. | |
| 15 | 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 | 18 | Verify squash commits with the `allowed_signers` line from `ermite signing-key show`: | |
| 19 | 19 | ||
| 20 | 20 | ```sh | |
| 21 | 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. | |
| @@ -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 | ```sh | 20 | ```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 | |||
| 6 | 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 | 11 | | Preset | For | | |
| 12 | 12 | |---|---| | |
| @@ -28,7 +28,7 @@ ermite token create claude --preset agent --scope repo:read --repo myrepo --bran | |||
| 28 | 28 | The token **can**: | |
| 29 | 29 | ||
| 30 | 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 | 32 | - with `mr:merge`, merge into a branch under its prefixes; | |
| 33 | 33 | - read CI runs, logs and checks; with `ci:write`, cancel and re-run runs of its own branches; | |
| 34 | 34 | - 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): | |||
| 28 | 28 | ||
| 29 | 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 | 32 | | `agent` | `repo:write`, `mr:write` | | |
| 33 | 33 | | `ci` | `repo:read` | | |
| 34 | 34 | ||
| @@ -39,7 +39,8 @@ Scopes: | |||
| 39 | 39 | | `repo:read` | read private repositories | | |
| 40 | 40 | | `repo:write` | push, create a repository | | |
| 41 | 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 | 44 | | `mr:merge` | merge, and delete the merged source branch with `--delete-branch` | | |
| 44 | 45 | | `token:admin` | create, list and revoke tokens | | |
| 45 | 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 | 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 | 57 | | `mr list <repo> [--state open\|merged\|closed\|all] [--json]` | none | default `open` | | |
| 57 | 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 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 | | |
| 60 | 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 | 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 | 70 | ## Workflows | |
| 66 | 71 | ||
| @@ -82,7 +87,7 @@ Workflow JSON: `repo`, `file` (always `.github/workflows/<file>`), `name` (the ` | |||
| 82 | 87 | | `repo_created`, `repo_updated`, `repo_deleted` | `repo`, `repository` | | |
| 83 | 88 | | `pushed` | `repo`, `ref_updates` (`ref`, `old_id`, `new_id`; an empty id means created or deleted) | | |
| 84 | 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 | 91 | | `gap` | none: events were lost, reload the state | | |
| 87 | 92 | ||
| 88 | 93 | ## 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 | ## Workflows | 70 | ## 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 web | 93 | ## 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, | |||
| 35 | 35 | ||
| 36 | 36 | 1. List: `ermite mr list <repo> --json`. | |
| 37 | 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. | |
| 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. | |
| 40 | 41 | ||
| 41 | 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 | 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 | 54 | ## Tokens | |
| @@ -48,11 +57,11 @@ Open one: `ermite mr create <repo> --source <branch> --title "..."`. After new p | |||
| 48 | 57 | 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 | 62 | ## 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 | 66 | ## Run a server | |
| 58 | 67 | ||
| @@ -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 | ## Tokens | 54 | ## 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 30d | 57 | 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 events | 62 | ## 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 server | 66 | ## Run a server | ||
| 58 | 67 | ||||