Happy review
This commit is contained in:
+6
-5
@@ -1015,13 +1015,14 @@ components:
|
||||
id: { type: string }
|
||||
task_id: { type: string }
|
||||
snapshot_n: { type: integer }
|
||||
agent_session_id: { type: string, description: The reviewer's session. }
|
||||
vendor: { type: string }
|
||||
model: { type: string }
|
||||
agent_session_id: { type: string, description: "The reviewer's session; empty when skipped." }
|
||||
vendor: { type: string, description: "Empty when skipped." }
|
||||
model: { type: string, description: "Empty when skipped." }
|
||||
state:
|
||||
type: string
|
||||
enum: [running, done, failed]
|
||||
x-enum-varnames: [ReviewStateRunning, ReviewStateDone, ReviewStateFailed]
|
||||
enum: [running, done, failed, skipped]
|
||||
x-enum-varnames: [ReviewStateRunning, ReviewStateDone, ReviewStateFailed, ReviewStateSkipped]
|
||||
description: "skipped: the snapshot's step changed no files, so no reviewer ran."
|
||||
result: { type: object, description: "done: the reviewer's submit_review arguments: summary (Markdown) and findings, each with severity, category, title, detail, path and line." }
|
||||
error: { type: string, description: "failed: why." }
|
||||
created_at: { type: string, format: date-time }
|
||||
|
||||
+3
-3
@@ -238,14 +238,14 @@ The fork's session guidance gets one more line (`forkGuidance` in `internal/agen
|
||||
|
||||
## Reviews
|
||||
|
||||
Every new snapshot of a task gets a review: a separate agent session on the vendor the task's agent doesn't use reads the change and reports on it (`internal/agents/review.go`, `internal/store/review.go`). A turn that changes no files makes no snapshot and so no review, and a snapshot whose change (the diff below) has no files, such as a task's first snapshot taken before its agent changed anything, gets no review either. Nothing else starts a review, and nothing waits for one: reviews ride along for the human to look at when they want.
|
||||
Every new snapshot of a task gets a review: a separate agent session on the vendor the task's agent doesn't use reads the change and reports on it (`internal/agents/review.go`, `internal/store/review.go`). A turn that changes no files makes no snapshot and so no review, and a snapshot whose change (the diff below) has no files, such as a task's first snapshot taken before its agent changed anything, runs no reviewer: it gets a `reviews` row in state `skipped`, with no session, vendor or model. Nothing else starts a review, and nothing waits for one: reviews ride along for the human to look at when they want.
|
||||
|
||||
- **Starting.** After `takeSnapshot` creates a snapshot, `afterSnapshot` runs `startReviewer`: it picks the vendor opposite the task's latest implementer session (`reviewVendor`) and that vendor's model in `reviewModels`, checks the snapshot's commit out detached at `worktrees/<project-id>/review-<review-id>`, creates an `agent_sessions` row with `role` `reviewer` on the task (created by the task's creator), a `reviews` row (`running`, one per snapshot), and sends `reviewPrompt` as its first turn (actor `byre`): the change as `git diff` from the previous snapshot (the snapshot commit's parent for the first) with its changed files, the merge-base diff of all the task's work for context, and the task's first message to its agent.
|
||||
- **Running.** Reviewer sessions get `reviewGuidance` and only the `submit_review` tool, whose arguments (`reviewSchema`: `change`, a few sentences of Markdown on what the change does, required and non-empty; a Markdown `summary`, which the guidance and schema require to be exactly "No significant findings." when there are no findings and otherwise a bulleted list with one line per finding; `findings`, each with `severity`, `category`, `title`, `detail`, `path`, `line`, `end_line` and `images`; and top-level `images`) become the review's `result` and mark it `done`. The guidance encourages, without requiring, Mermaid blocks for a data flow, call graph, state machine or schema the change alters, and screenshots when the change alters something visible.
|
||||
- **Screenshots.** Reviewer sessions also get the Playwright MCP server (`playwrightMCP` in `internal/agents/review.go`: `npx @playwright/mcp` at a pinned version, headless Chrome, isolated profile, absolute file paths), passed to Claude as a `stdio` entry in `--mcp-config` and to Codex as `mcp_servers` in the thread's `config`. Its automatically named files go to `worktrees/<project-id>/review-<review-id>-media`, removed with the checkout. Each `images` entry is `{path, caption}`, the path absolute or relative to the checkout; at `submit_review` byre reads every one, refuses the whole submission (nothing recorded) for a missing file or one that isn't one of `store.ImageTypes` within `store.MaxBlobBytes`, then stores each as a blob and replaces its `path` with `blob`, since the files are deleted with the checkout. They run without the Stop-hook bounce, mid-turn delivery, end-of-turn commit, snapshots or turn summaries, and their `user.message` events carry no `head`. When the reviewer's turn ends byre completes its session, kills its process and removes the worktree; a review still `running` then fails with why (no submission, with the final message; an error; an interrupt). A process that exits mid-turn fails the review the same way. Merging or discarding the task fails its running reviews ("the task was closed") and ends their reviewers. On startup, a running reviewer resumes like any session, and a running review whose reviewer isn't `running` fails.
|
||||
- **Questions.** A question's `reviews` is the review of the first snapshot the task took after the question was filed, the one at the end of the turn that asked it, computed when the item is read; the Inbox tab shows it under the question once it starts.
|
||||
- **Events.** `review.started` and `review.finished` (state `done` or `failed`) carry the whole review, with the task's ids and no `agent_session_id`. A reviewer's own events carry its session id and the task's ids: the transcript skips every event of a session whose `session.created` has `role` `reviewer`, and draws each `review.finished` as a note with its outcome; the lead's `read_transcript` leaves the reviewer's events out and shows each `review.finished` with its summary and findings (`reviewLine`). `GET /api/tasks/{taskId}/sessions` and everything that looks for the task's agent (`ListSessions`) see only implementer sessions.
|
||||
- **UI.** The **Reviews** tab (`web/src/reviews.ts`) shows one review at a time, picked by snapshot (see "Snapshots" for the picker): its snapshot, state or severity counts, error, `change`, summary, screenshots and findings. A finding with `path` and `line` shows, under its title, the lines `line` to `end_line` plus 3 either side from that file's diff for the snapshot's step (`changes?snapshot=<n>&step=true`, the diff the reviewer was given), with new-file line numbers and the finding's own lines marked; it shows nothing when those lines aren't in the diff, and "diff not available" with the error once the task is closed. Screenshots link to the full image. Each finding has a **fix** button (also on review cards in the inbox) that sends the finding's severity, title, location and detail to the task's agent as a message from the user: through `POST /api/sessions/{id}/messages` when its latest live session is idle, starting a session when it has none, and refused with an alert while the agent is mid-turn. It loads when it opens, when the task changes, and on each `review.*` event in the task's stream. When the finished review of the highest-numbered snapshot has findings, the tab (or the More menu holding it) is highlighted with a badge of the finding count. Each snapshot row's **review** button gets the same treatment from that snapshot's `review.finished`: highlighted, with the count in a badge over its top-right corner. `GET /api/tasks/{taskId}/reviews` lists a task's reviews oldest first. `sqlite3 byre.db "SELECT id, task_id, snapshot_n, vendor, state, error FROM reviews ORDER BY created_at DESC LIMIT 20"` shows recent ones.
|
||||
- **Events.** `review.started` and `review.finished` (state `done`, `failed` or `skipped`; a skipped review emits only `review.finished`) carry the whole review, with the task's ids and no `agent_session_id`. A reviewer's own events carry its session id and the task's ids: the transcript skips every event of a session whose `session.created` has `role` `reviewer`, and draws each `review.finished` as a note with its outcome; the lead's `read_transcript` leaves the reviewer's events out and shows each `review.finished` with its summary and findings (`reviewLine`). `GET /api/tasks/{taskId}/sessions` and everything that looks for the task's agent (`ListSessions`) see only implementer sessions.
|
||||
- **UI.** The **Reviews** tab (`web/src/reviews.ts`) shows one review at a time, picked by snapshot (see "Snapshots" for the picker): its snapshot, state or severity counts, error, `change`, summary, screenshots and findings. A finding with `path` and `line` shows, under its title, the lines `line` to `end_line` plus 3 either side from that file's diff for the snapshot's step (`changes?snapshot=<n>&step=true`, the diff the reviewer was given), with new-file line numbers and the finding's own lines marked; it shows nothing when those lines aren't in the diff, and "diff not available" with the error once the task is closed. Screenshots link to the full image. Each finding has a **fix** button (also on review cards in the inbox) that sends the finding's severity, title, location and detail to the task's agent as a message from the user: through `POST /api/sessions/{id}/messages` when its latest live session is idle, starting a session when it has none, and refused with an alert while the agent is mid-turn. It loads when it opens, when the task changes, and on each `review.*` event in the task's stream. When the finished review of the highest-numbered snapshot has findings, the tab (or the More menu holding it) is highlighted with a badge of the finding count. Each snapshot row's **review** button carries a mark over its top-right corner from that snapshot's latest `review.*` event: a spinner while the review runs, a grey ● when it was skipped, a green ✓ when it finished with no findings, and, when it has findings, the button highlighted with the count in a badge. A failed review leaves the button unmarked. `GET /api/tasks/{taskId}/reviews` lists a task's reviews oldest first. `sqlite3 byre.db "SELECT id, task_id, snapshot_n, vendor, state, error FROM reviews ORDER BY created_at DESC LIMIT 20"` shows recent ones.
|
||||
|
||||
## Events
|
||||
|
||||
|
||||
@@ -142,6 +142,18 @@ func (m *Manager) startReviewer(ctx context.Context, task api.Task, snap api.Sna
|
||||
return err
|
||||
}
|
||||
if prompt == "" {
|
||||
now := time.Now().UTC()
|
||||
err = m.store.CreateReview(ctx, api.Review{
|
||||
Id: store.NewID(),
|
||||
TaskId: task.Id,
|
||||
SnapshotN: snap.N,
|
||||
State: api.ReviewStateSkipped,
|
||||
CreatedAt: now,
|
||||
FinishedAt: &now,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
slog.Info("review skipped, snapshot has no diff", "task", task.Id, "snapshot", snap.N)
|
||||
return nil
|
||||
}
|
||||
@@ -494,6 +506,9 @@ func reviewLine(r api.Review) string {
|
||||
if r.State == api.ReviewStateFailed {
|
||||
return fmt.Sprintf("review of snapshot %d failed: %s", r.SnapshotN, clip(r.Error, 300))
|
||||
}
|
||||
if r.State == api.ReviewStateSkipped {
|
||||
return fmt.Sprintf("review of snapshot %d skipped: no changes", r.SnapshotN)
|
||||
}
|
||||
res := map[string]any{}
|
||||
if r.Result != nil {
|
||||
res = *r.Result
|
||||
|
||||
+173
-163
@@ -278,6 +278,7 @@ const (
|
||||
ReviewStateDone ReviewState = "done"
|
||||
ReviewStateFailed ReviewState = "failed"
|
||||
ReviewStateRunning ReviewState = "running"
|
||||
ReviewStateSkipped ReviewState = "skipped"
|
||||
)
|
||||
|
||||
// Valid indicates whether the value is a known member of the ReviewState enum.
|
||||
@@ -289,6 +290,8 @@ func (e ReviewState) Valid() bool {
|
||||
return true
|
||||
case ReviewStateRunning:
|
||||
return true
|
||||
case ReviewStateSkipped:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -748,7 +751,7 @@ type ProtocolErrors struct {
|
||||
|
||||
// Review An agent session on the vendor the task's agent doesn't use, reviewing one snapshot's change.
|
||||
type Review struct {
|
||||
// AgentSessionId The reviewer's session.
|
||||
// AgentSessionId The reviewer's session; empty when skipped.
|
||||
AgentSessionId string `json:"agent_session_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
|
||||
@@ -756,17 +759,23 @@ type Review struct {
|
||||
Error string `json:"error"`
|
||||
FinishedAt *time.Time `json:"finished_at,omitempty"`
|
||||
Id string `json:"id"`
|
||||
Model string `json:"model"`
|
||||
|
||||
// Model Empty when skipped.
|
||||
Model string `json:"model"`
|
||||
|
||||
// Result done: the reviewer's submit_review arguments: summary (Markdown) and findings, each with severity, category, title, detail, path and line.
|
||||
Result *map[string]interface{} `json:"result,omitempty"`
|
||||
SnapshotN int `json:"snapshot_n"`
|
||||
State ReviewState `json:"state"`
|
||||
TaskId string `json:"task_id"`
|
||||
Vendor string `json:"vendor"`
|
||||
|
||||
// State skipped: the snapshot's step changed no files, so no reviewer ran.
|
||||
State ReviewState `json:"state"`
|
||||
TaskId string `json:"task_id"`
|
||||
|
||||
// Vendor Empty when skipped.
|
||||
Vendor string `json:"vendor"`
|
||||
}
|
||||
|
||||
// ReviewState defines model for Review.State.
|
||||
// ReviewState skipped: the snapshot's step changed no files, so no reviewer ran.
|
||||
type ReviewState string
|
||||
|
||||
// SessionCreate defines model for SessionCreate.
|
||||
@@ -2715,163 +2724,164 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H
|
||||
// const string: with thousands of chunks the chained `+` fold is several
|
||||
// times slower for the Go compiler than parsing a slice literal.
|
||||
var swaggerSpec = []string{
|
||||
"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",
|
||||
}
|
||||
|
||||
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
|
||||
|
||||
@@ -10,18 +10,25 @@ import (
|
||||
"g.fc.run/misc/byre/internal/api"
|
||||
)
|
||||
|
||||
const reviewColumns = `id, task_id, snapshot_n, agent_session_id, vendor, model, state, result, error, created_at, finished_at`
|
||||
const reviewColumns = `id, task_id, snapshot_n, coalesce(agent_session_id, ''), vendor, model, state, result, error, created_at, finished_at`
|
||||
|
||||
func (s *Store) CreateReview(ctx context.Context, r api.Review) error {
|
||||
finishedAt := ""
|
||||
if r.FinishedAt != nil {
|
||||
finishedAt = formatTime(*r.FinishedAt)
|
||||
}
|
||||
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
|
||||
_, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO reviews (id, task_id, snapshot_n, agent_session_id, vendor, model, state, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`, r.Id, r.TaskId, r.SnapshotN, r.AgentSessionId, r.Vendor, r.Model, string(r.State), formatTime(r.CreatedAt))
|
||||
INSERT INTO reviews (id, task_id, snapshot_n, agent_session_id, vendor, model, state, created_at, finished_at)
|
||||
VALUES (?, ?, ?, nullif(?, ''), ?, ?, ?, ?, ?)
|
||||
`, r.Id, r.TaskId, r.SnapshotN, r.AgentSessionId, r.Vendor, r.Model, string(r.State), formatTime(r.CreatedAt), finishedAt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return emitReview(ctx, tx, emit, "review.started", r.Id)
|
||||
if r.State == api.ReviewStateRunning {
|
||||
return emitReview(ctx, tx, emit, "review.started", r.Id)
|
||||
}
|
||||
return emitReview(ctx, tx, emit, "review.finished", r.Id)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -321,6 +321,25 @@ DROP TABLE vapid_keys;
|
||||
`}, {"delete-report-inbox-items", `
|
||||
DELETE FROM inbox_responses WHERE item_id IN (SELECT id FROM inbox_items WHERE kind = 'report');
|
||||
DELETE FROM inbox_items WHERE kind = 'report';
|
||||
`}, {"allow-sessionless-reviews", `
|
||||
CREATE TABLE reviews_new (
|
||||
id TEXT PRIMARY KEY,
|
||||
task_id TEXT NOT NULL REFERENCES tasks(id),
|
||||
snapshot_n INTEGER NOT NULL,
|
||||
agent_session_id TEXT REFERENCES agent_sessions(id),
|
||||
vendor TEXT NOT NULL,
|
||||
model TEXT NOT NULL,
|
||||
state TEXT NOT NULL,
|
||||
result TEXT NOT NULL DEFAULT '',
|
||||
error TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL,
|
||||
finished_at TEXT NOT NULL DEFAULT '',
|
||||
UNIQUE (task_id, snapshot_n)
|
||||
);
|
||||
INSERT INTO reviews_new SELECT * FROM reviews;
|
||||
DROP TABLE reviews;
|
||||
ALTER TABLE reviews_new RENAME TO reviews;
|
||||
CREATE UNIQUE INDEX reviews_session ON reviews(agent_session_id);
|
||||
`}}
|
||||
|
||||
const (
|
||||
|
||||
Vendored
+2
@@ -31,6 +31,8 @@ function stateText(r) {
|
||||
return reviewCounts(r);
|
||||
case "failed":
|
||||
return "failed";
|
||||
case "skipped":
|
||||
return "skipped: no changes";
|
||||
}
|
||||
}
|
||||
export function reviewCard(r, taskId) {
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+22
-5
@@ -360,11 +360,11 @@ export function mountChat(root, src, makeHead) {
|
||||
put(snapRow(p, ctx));
|
||||
continue;
|
||||
}
|
||||
if (ev.kind === "review.finished") {
|
||||
if (ev.kind === "review.started" || ev.kind === "review.finished") {
|
||||
const r = p;
|
||||
const button = ctx.reviewButtons.get(r.snapshot_n);
|
||||
if (button) {
|
||||
markFindings(button, findingCount(r));
|
||||
markReview(button, r);
|
||||
}
|
||||
}
|
||||
if (ev.kind === "agent.tool_call" && isSubagentCall(p)) {
|
||||
@@ -717,9 +717,26 @@ function claudeTaskStatus(p, ctx) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
function markFindings(button, n) {
|
||||
replace(button, "review", n > 0 && h("span", { class: "badge", title: `${n} findings` }, n));
|
||||
function markReview(button, r) {
|
||||
const n = findingCount(r);
|
||||
button.classList.toggle("attention", n > 0);
|
||||
if (r.state === "running") {
|
||||
replace(button, "review", h("span", { class: "review-mark spinner", title: "review in progress" }));
|
||||
return;
|
||||
}
|
||||
if (r.state === "failed") {
|
||||
replace(button, "review");
|
||||
return;
|
||||
}
|
||||
if (r.state === "skipped") {
|
||||
replace(button, "review", h("span", { class: "review-mark skipped", title: "skipped: no changes" }, "●"));
|
||||
return;
|
||||
}
|
||||
if (n === 0) {
|
||||
replace(button, "review", h("span", { class: "review-mark clean", title: "no findings" }, "✓"));
|
||||
return;
|
||||
}
|
||||
replace(button, "review", h("span", { class: "badge", title: `${n} findings` }, n));
|
||||
}
|
||||
function snapRow(snap, ctx) {
|
||||
const taskId = ctx.taskId;
|
||||
@@ -855,7 +872,7 @@ function renderEvent(ev, ctx) {
|
||||
return h("div", { class: "note" }, `${ev.actor} set the merge commit title to "${String(p["merge_title"])}" `, when);
|
||||
case "review.finished": {
|
||||
const r = p;
|
||||
const outcome = r.state === "failed" ? `failed: ${r.error}` : reviewCounts(r);
|
||||
const outcome = r.state === "failed" ? `failed: ${r.error}` : r.state === "skipped" ? "skipped: no changes" : reviewCounts(r);
|
||||
return h("div", { class: `note${r.state === "failed" ? " error" : ""}` }, `review of snapshot #${r.snapshot_n}: ${outcome} `, when);
|
||||
}
|
||||
case "task.merge_queued":
|
||||
|
||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+8
-3
@@ -942,12 +942,17 @@ export interface components {
|
||||
id: string;
|
||||
task_id: string;
|
||||
snapshot_n: number;
|
||||
/** @description The reviewer's session. */
|
||||
/** @description The reviewer's session; empty when skipped. */
|
||||
agent_session_id: string;
|
||||
/** @description Empty when skipped. */
|
||||
vendor: string;
|
||||
/** @description Empty when skipped. */
|
||||
model: string;
|
||||
/** @enum {string} */
|
||||
state: "running" | "done" | "failed";
|
||||
/**
|
||||
* @description skipped: the snapshot's step changed no files, so no reviewer ran.
|
||||
* @enum {string}
|
||||
*/
|
||||
state: "running" | "done" | "failed" | "skipped";
|
||||
/** @description done: the reviewer's submit_review arguments: summary (Markdown) and findings, each with severity, category, title, detail, path and line. */
|
||||
result?: Record<string, never>;
|
||||
/** @description failed: why. */
|
||||
|
||||
@@ -40,6 +40,8 @@ function stateText(r: Review): string {
|
||||
return reviewCounts(r);
|
||||
case "failed":
|
||||
return "failed";
|
||||
case "skipped":
|
||||
return "skipped: no changes";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-5
@@ -439,11 +439,11 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
|
||||
put(snapRow(p as unknown as Snapshot, ctx));
|
||||
continue;
|
||||
}
|
||||
if (ev.kind === "review.finished") {
|
||||
if (ev.kind === "review.started" || ev.kind === "review.finished") {
|
||||
const r = p as unknown as Review;
|
||||
const button = ctx.reviewButtons.get(r.snapshot_n);
|
||||
if (button) {
|
||||
markFindings(button, findingCount(r));
|
||||
markReview(button, r);
|
||||
}
|
||||
}
|
||||
if (ev.kind === "agent.tool_call" && isSubagentCall(p)) {
|
||||
@@ -846,9 +846,26 @@ function claudeTaskStatus(p: Record<string, unknown>, ctx: Ctx): void {
|
||||
}
|
||||
}
|
||||
|
||||
function markFindings(button: HTMLElement, n: number): void {
|
||||
replace(button, "review", n > 0 && h("span", { class: "badge", title: `${n} findings` }, n));
|
||||
function markReview(button: HTMLElement, r: Review): void {
|
||||
const n = findingCount(r);
|
||||
button.classList.toggle("attention", n > 0);
|
||||
if (r.state === "running") {
|
||||
replace(button, "review", h("span", { class: "review-mark spinner", title: "review in progress" }));
|
||||
return;
|
||||
}
|
||||
if (r.state === "failed") {
|
||||
replace(button, "review");
|
||||
return;
|
||||
}
|
||||
if (r.state === "skipped") {
|
||||
replace(button, "review", h("span", { class: "review-mark skipped", title: "skipped: no changes" }, "●"));
|
||||
return;
|
||||
}
|
||||
if (n === 0) {
|
||||
replace(button, "review", h("span", { class: "review-mark clean", title: "no findings" }, "✓"));
|
||||
return;
|
||||
}
|
||||
replace(button, "review", h("span", { class: "badge", title: `${n} findings` }, n));
|
||||
}
|
||||
|
||||
function snapRow(snap: Snapshot, ctx: Ctx): HTMLElement {
|
||||
@@ -1015,7 +1032,7 @@ function renderEvent(ev: ByreEvent, ctx: Ctx): HTMLElement | null {
|
||||
|
||||
case "review.finished": {
|
||||
const r = p as unknown as Review;
|
||||
const outcome = r.state === "failed" ? `failed: ${r.error}` : reviewCounts(r);
|
||||
const outcome = r.state === "failed" ? `failed: ${r.error}` : r.state === "skipped" ? "skipped: no changes" : reviewCounts(r);
|
||||
return h("div", { class: `note${r.state === "failed" ? " error" : ""}` }, `review of snapshot #${r.snapshot_n}: ${outcome} `, when);
|
||||
}
|
||||
|
||||
|
||||
@@ -957,6 +957,32 @@ button.compact.danger {
|
||||
line-height: 14px;
|
||||
}
|
||||
|
||||
.snap-row button .review-mark {
|
||||
position: absolute;
|
||||
top: -7px;
|
||||
right: -7px;
|
||||
}
|
||||
|
||||
.snap-row button .review-mark.spinner {
|
||||
margin: 0;
|
||||
background: var(--muted);
|
||||
}
|
||||
|
||||
.snap-row button .review-mark.clean,
|
||||
.snap-row button .review-mark.skipped {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.snap-row button .review-mark.clean {
|
||||
color: var(--ok);
|
||||
}
|
||||
|
||||
.snap-row button .review-mark.skipped {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.note.ask {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user