kaloko 0.4.7 → 0.4.9
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 +41 -2
- package/package.json +1 -1
- package/schemas/config.v1.json +26 -0
- package/schemas/scenario.v1.json +32 -0
- package/schemas/spec.v1.json +60 -0
- package/skills/kaloko/SKILL.md +24 -7
- package/skills/kaloko/reference/acceptance.md +34 -2
- package/skills/kaloko/reference/collaboration.md +45 -2
- package/skills/kaloko/reference/design.md +35 -2
- package/skills/kaloko/reference/evaluation.md +51 -3
- package/skills/kaloko/reference/execution.md +18 -0
- package/skills/kaloko/reference/first-run.md +51 -0
- package/skills/kaloko/reference/mcp.md +8 -1
- package/src/cli.mjs +93 -9
- package/src/lib/build.mjs +30 -5
- package/src/lib/capture.mjs +43 -1
- package/src/lib/design-cli.mjs +7 -3
- package/src/lib/devspec.mjs +821 -0
- package/src/lib/doctor.mjs +7 -0
- package/src/lib/drift.mjs +16 -8
- package/src/lib/evaluate.mjs +94 -12
- package/src/lib/keyboard.mjs +261 -0
- package/src/lib/manifest-core.mjs +16 -5
- package/src/lib/onboarding.mjs +33 -0
- package/src/lib/pack-help.mjs +7 -0
- package/src/lib/packs.mjs +9 -2
- package/src/lib/png.mjs +61 -0
- package/src/lib/probe.mjs +57 -0
- package/src/lib/recheck.mjs +3 -1
- package/src/lib/review.mjs +69 -0
- package/src/lib/spec-cli.mjs +90 -0
- package/src/lib/stability-core.mjs +114 -0
- package/src/lib/stability.mjs +249 -0
- package/src/lib/vision-core.mjs +132 -0
- package/src/lib/vision.mjs +127 -0
- package/templates/kaloko.config.yml +6 -0
- package/viewer/index.html +889 -46
package/README.md
CHANGED
|
@@ -64,6 +64,32 @@ npx kaloko share --pr # upload to kaloko.app, comment on the PR
|
|
|
64
64
|
`npx kaloko help` lists every command. When something does not work (Chrome,
|
|
65
65
|
token, environment URL, evaluator key), `npx kaloko doctor` says what to fix.
|
|
66
66
|
|
|
67
|
+
## Review only what changed
|
|
68
|
+
|
|
69
|
+
After `kaloko share`, steps that look exactly as they did in the last accepted
|
|
70
|
+
run (same screenshots, same criteria, automatic checks passing) keep the
|
|
71
|
+
approval a person gave them, with who and when. The rest waits in review
|
|
72
|
+
mode: one step at a time on the canvas, `J`/`K` to move, `A` to approve, `R`
|
|
73
|
+
to return with a note. The share says it in one line:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
12 steps unchanged, approvals carried over; 5 need review: https://kaloko.app/acme/runs/…/#review
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`npx kaloko review` (or `--json`, or MCP `get_review_queue`) tells an agent
|
|
80
|
+
what still waits for a person, so it can pass the link on. Agents never
|
|
81
|
+
approve for people: their decisions are marked and do not count as a
|
|
82
|
+
person's.
|
|
83
|
+
|
|
84
|
+
Every AI verdict comes with one sentence saying why. Criteria marked
|
|
85
|
+
`vision: true`, and answers the text evaluator is unsure about, get a second
|
|
86
|
+
look at the screenshot from a vision model (`ANTHROPIC_API_KEY`, or through
|
|
87
|
+
Kaloko); `kaloko build` lists what to act on with the score and the reason.
|
|
88
|
+
The `a11y` pack walks the page with Tab (order, visible focus, traps, skip
|
|
89
|
+
link) and reads names from the accessibility tree. Pixel diffs skip the
|
|
90
|
+
regions a step ignores; `kaloko walk --stability 3` and `kaloko stability`
|
|
91
|
+
find the clocks, ads and carousels worth ignoring.
|
|
92
|
+
|
|
67
93
|
## Design (Kaloko Design)
|
|
68
94
|
|
|
69
95
|
Design the screens of a flow as live HTML with your own agent and skills, in the same scenario that development
|
|
@@ -81,6 +107,17 @@ npx kaloko share --name "Checkout v2" # a version of the whole flow
|
|
|
81
107
|
npx kaloko reference --design --version 2 # after the team accepted it
|
|
82
108
|
```
|
|
83
109
|
|
|
110
|
+
Developers and their agents build from what was approved. *Dev mode* (`D` in the step detail) shows each
|
|
111
|
+
element's box, sizes and distances, its colors, type and spacing with the token every value comes from (or a
|
|
112
|
+
warning when it is not a token), contrast against WCAG AA, the `:hover`, `:focus`, `:active` and disabled states,
|
|
113
|
+
and its images. The same data is the step spec an agent reads, and `kaloko drift` checks the implementation
|
|
114
|
+
against it:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npx kaloko spec payment # elements, styles → tokens, states (also MCP get_step_spec)
|
|
118
|
+
npx kaloko walk && npx kaloko drift # expected token vs actual value per element; exit 1 while it differs
|
|
119
|
+
```
|
|
120
|
+
|
|
84
121
|
On the canvas, *Prototype* (or `P`) presents the steps full screen as live pages: click through links between
|
|
85
122
|
steps, switch the device, walk with the arrow keys, and share a link that opens straight into a step. The rules
|
|
86
123
|
for pages that work everywhere are in `skills/kaloko/reference/portable-html.md`.
|
|
@@ -121,10 +158,12 @@ captured HTML, page text and console output.
|
|
|
121
158
|
- **E-mails**: built-in Mailpit client; any other mailbox through a small
|
|
122
159
|
adapter module (`provider: script`, example in `templates/mail/`), or hand a
|
|
123
160
|
message over with `npx kaloko mail --step <id> --file message.eml`.
|
|
124
|
-
- **Service and evaluator**: `KALOKO_TOKEN` for `share`, `feedback`, `inbox` and clean-up (`runs`, `trash`, `prune`, `keep`, `archive`)
|
|
161
|
+
- **Service and evaluator**: `KALOKO_TOKEN` for `share`, `feedback`, `review`, `inbox` and clean-up (`runs`, `trash`, `prune`, `keep`, `archive`)
|
|
125
162
|
(`npx kaloko login` approves one in the browser and saves it to `~/.config/kaloko/.env`, or create it in your
|
|
126
163
|
organization's settings); `TYPESAFE_API_KEY` for the
|
|
127
|
-
semantic evaluator
|
|
164
|
+
semantic evaluator and `ANTHROPIC_API_KEY` for the vision evaluator (both
|
|
165
|
+
optional; without them Kaloko evaluates on your plan's allowance). The names
|
|
166
|
+
can be changed in the config.
|
|
128
167
|
|
|
129
168
|
`npx kaloko doctor` checks that every variable the config names is set and that
|
|
130
169
|
each environment lets you in.
|
package/package.json
CHANGED
package/schemas/config.v1.json
CHANGED
|
@@ -45,6 +45,32 @@
|
|
|
45
45
|
"signals": { "type": "boolean", "default": true, "description": "Quick signals on every screen (purpose met, expected language, looks broken); never change verdicts." },
|
|
46
46
|
"concurrency": { "type": "integer", "minimum": 1, "maximum": 16, "default": 6, "description": "Evaluator requests in flight at once." }
|
|
47
47
|
}
|
|
48
|
+
},
|
|
49
|
+
"vision": {
|
|
50
|
+
"description": "Second, vision-capable evaluator (#17): sees the screenshot (a crop for scoped criteria) and answers with a probability and a one-sentence reason. Asked only for criteria with vision: true and for text answers in the uncertain band; false turns it off.",
|
|
51
|
+
"oneOf": [
|
|
52
|
+
{ "const": false },
|
|
53
|
+
{
|
|
54
|
+
"type": "object",
|
|
55
|
+
"additionalProperties": false,
|
|
56
|
+
"properties": {
|
|
57
|
+
"provider": { "enum": ["anthropic"], "default": "anthropic" },
|
|
58
|
+
"model": { "type": "string", "default": "claude-opus-5-5", "description": "Model id of the provider (Anthropic Messages API with image input)." },
|
|
59
|
+
"api_key_env": { "type": "string", "default": "ANTHROPIC_API_KEY" },
|
|
60
|
+
"base_url": { "type": "string", "default": "https://api.anthropic.com" },
|
|
61
|
+
"when": { "enum": ["uncertain", "flagged", "always", "off"], "default": "uncertain", "description": "flagged: only criteria with vision: true; uncertain: those plus text answers in the uncertain band; always: every semantic criterion (most expensive)." },
|
|
62
|
+
"uncertain": { "type": "array", "items": { "type": "number", "minimum": 0, "maximum": 1 }, "minItems": 2, "maxItems": 2, "default": [0.15, 0.85], "description": "Text scores strictly between these go to the vision evaluator." },
|
|
63
|
+
"max_calls": { "type": "integer", "minimum": 0, "default": 40, "description": "Vision requests per evaluation (uncached); the rest stay with the text answer." },
|
|
64
|
+
"max_width": { "type": "integer", "minimum": 320, "maximum": 1568, "default": 1000, "description": "Images are scaled down to this width (px)." },
|
|
65
|
+
"tile_height": { "type": "integer", "minimum": 400, "maximum": 1568, "default": 1400, "description": "A full page is cut into tiles of this height." },
|
|
66
|
+
"max_tiles": { "type": "integer", "minimum": 1, "maximum": 4, "default": 2, "description": "Tiles of a full page sent at most (from the top); scoped criteria get a crop instead." },
|
|
67
|
+
"effort": { "enum": ["low", "medium", "high"], "default": "low" },
|
|
68
|
+
"reason_language": { "type": "string", "default": "en", "description": "Language of the one-sentence reasons (en, cs, …)." },
|
|
69
|
+
"hosted": { "type": "boolean", "default": true, "description": "Without a local key, ask through the organization's service (counts against the monthly allowance; one vision question counts as 5)." },
|
|
70
|
+
"concurrency": { "type": "integer", "minimum": 1, "maximum": 8, "default": 3 }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
]
|
|
48
74
|
}
|
|
49
75
|
}
|
|
50
76
|
},
|
package/schemas/scenario.v1.json
CHANGED
|
@@ -262,6 +262,11 @@
|
|
|
262
262
|
"capture": {
|
|
263
263
|
"$ref": "#/$defs/capture"
|
|
264
264
|
},
|
|
265
|
+
"ignore": {
|
|
266
|
+
"type": "array",
|
|
267
|
+
"description": "Regions left out of pixel comparisons (clocks, ads, carousels): a selector, or a rectangle in page pixels of one viewport. kaloko stability suggests them.",
|
|
268
|
+
"items": { "$ref": "#/$defs/ignoreRegion" }
|
|
269
|
+
},
|
|
265
270
|
"items": {
|
|
266
271
|
"type": "object",
|
|
267
272
|
"additionalProperties": false,
|
|
@@ -500,6 +505,13 @@
|
|
|
500
505
|
"a11y_contrast",
|
|
501
506
|
"a11y_main",
|
|
502
507
|
"a11y_unique_ids",
|
|
508
|
+
"a11y_focus_order",
|
|
509
|
+
"a11y_focus_visible",
|
|
510
|
+
"a11y_keyboard_trap",
|
|
511
|
+
"a11y_skip_link",
|
|
512
|
+
"a11y_ax_names",
|
|
513
|
+
"a11y_landmarks",
|
|
514
|
+
"a11y_aria",
|
|
503
515
|
"perf_lcp",
|
|
504
516
|
"perf_cls",
|
|
505
517
|
"perf_ttfb",
|
|
@@ -588,6 +600,10 @@
|
|
|
588
600
|
"console"
|
|
589
601
|
],
|
|
590
602
|
"description": "console: the evaluator reads the captured console errors with the step purpose (triage) instead of the page"
|
|
603
|
+
},
|
|
604
|
+
"vision": {
|
|
605
|
+
"type": "boolean",
|
|
606
|
+
"description": "Also show the evaluator the screenshot (a crop of scope when set): layout, imagery, brand. Needs a vision evaluator (evaluators.vision)."
|
|
591
607
|
}
|
|
592
608
|
}
|
|
593
609
|
},
|
|
@@ -632,6 +648,22 @@
|
|
|
632
648
|
}
|
|
633
649
|
}
|
|
634
650
|
},
|
|
651
|
+
"ignoreRegion": {
|
|
652
|
+
"type": "object",
|
|
653
|
+
"additionalProperties": false,
|
|
654
|
+
"oneOf": [
|
|
655
|
+
{ "required": ["selector"] },
|
|
656
|
+
{ "required": ["rect", "viewport"] }
|
|
657
|
+
],
|
|
658
|
+
"properties": {
|
|
659
|
+
"selector": { "type": "string", "minLength": 1, "description": "CSS selector; every visible match is ignored." },
|
|
660
|
+
"rect": { "type": "array", "items": { "type": "number", "minimum": 0 }, "minItems": 4, "maxItems": 4, "description": "[x, y, width, height] in CSS pixels of the page." },
|
|
661
|
+
"viewport": { "type": "string", "description": "The viewport a rect belongs to." },
|
|
662
|
+
"viewports": { "type": "array", "items": { "type": "string" }, "description": "Only in these viewports (selector)." },
|
|
663
|
+
"locales": { "type": "array", "items": { "type": "string" }, "description": "Only in these locales." },
|
|
664
|
+
"note": { "type": "string" }
|
|
665
|
+
}
|
|
666
|
+
},
|
|
635
667
|
"capture": {
|
|
636
668
|
"type": "object",
|
|
637
669
|
"additionalProperties": false,
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://qawalk.com/schemas/spec.v1.json",
|
|
4
|
+
"title": "qawalk.spec.v1",
|
|
5
|
+
"description": "The step spec (#42 Dev mode): what to build for one step as it was approved. Written by the in-page engine src/lib/devspec.mjs at capture (steps/<step>/<locale>/<viewport>.spec.json, and the capture record of a design revision); the canvas Dev mode runs the same engine on the live page. Served by GET …/scenarios/<id>/steps/<step>/spec, MCP get_step_spec and kaloko spec. Elements are listed in page order; a style lists only what differs from what the element inherits (page, then the parent element).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema", "elements"],
|
|
8
|
+
"$defs": {
|
|
9
|
+
"value": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"description": "A computed value (colors as #rrggbb[aa], lengths in px) and the token it maps to. token: the DTCG path (several, space-separated, for a shorthand such as padding; 0 for a zero side); null when the set has tokens of that kind but none fits and none is near. nearest: the closest token when the value is not one. mode: the mode whose value it is (light, dark, …). hard: the stylesheet typed the raw value although the token exists. var: the custom property the author used when it names no token.",
|
|
12
|
+
"required": ["value"],
|
|
13
|
+
"properties": {
|
|
14
|
+
"value": { "type": "string" },
|
|
15
|
+
"token": { "type": ["string", "null"] },
|
|
16
|
+
"nearest": { "type": "string" },
|
|
17
|
+
"mode": { "type": "string" },
|
|
18
|
+
"hard": { "type": "boolean" },
|
|
19
|
+
"var": { "type": "string" }
|
|
20
|
+
},
|
|
21
|
+
"additionalProperties": false
|
|
22
|
+
},
|
|
23
|
+
"styles": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"description": "CSS property → value: color, background-color, border-color, font-family, font-size, font-weight, line-height, letter-spacing, padding, gap, row-gap, column-gap, border-width, border-radius, box-shadow, opacity, display and flex or grid properties.",
|
|
26
|
+
"additionalProperties": { "$ref": "#/$defs/value" }
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"properties": {
|
|
30
|
+
"schema": { "const": "qawalk.spec.v1" },
|
|
31
|
+
"title": { "type": "string" },
|
|
32
|
+
"lang": { "type": ["string", "null"] },
|
|
33
|
+
"viewport": { "type": "array", "items": { "type": "number" }, "minItems": 2, "maxItems": 2 },
|
|
34
|
+
"size": { "type": "array", "items": { "type": "number" }, "minItems": 2, "maxItems": 2, "description": "Document width and height." },
|
|
35
|
+
"tokens": { "type": ["object", "null"], "description": "The token set the values were mapped with (the step's tokens.json); null without one.", "properties": { "count": { "type": "integer" }, "modes": { "type": "array", "items": { "type": "string" } } } },
|
|
36
|
+
"page": { "$ref": "#/$defs/styles", "description": "Typography and text color of the page (body), inherited by every element that does not say otherwise." },
|
|
37
|
+
"elements": {
|
|
38
|
+
"type": "array",
|
|
39
|
+
"items": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"required": ["i", "parent", "tag", "sel", "styles"],
|
|
42
|
+
"properties": {
|
|
43
|
+
"i": { "type": "integer" },
|
|
44
|
+
"parent": { "type": "integer", "description": "Index of the nearest listed ancestor, -1 at the top." },
|
|
45
|
+
"tag": { "type": "string" },
|
|
46
|
+
"role": { "type": ["string", "null"], "description": "ARIA role, explicit or implicit (button, link, heading, main, textbox, …)." },
|
|
47
|
+
"name": { "type": ["string", "null"], "description": "Accessible name or own text (at most 80 characters)." },
|
|
48
|
+
"sel": { "type": "string", "description": "A CSS selector of the element in the design page (ids, data-testid and classes first)." },
|
|
49
|
+
"box": { "type": "array", "items": { "type": "number" }, "minItems": 4, "maxItems": 4, "description": "x, y, width, height in CSS px of the page." },
|
|
50
|
+
"styles": { "$ref": "#/$defs/styles" },
|
|
51
|
+
"contrast": { "type": "object", "properties": { "ratio": { "type": "number" }, "aa": { "type": "boolean" } } },
|
|
52
|
+
"states": { "type": "object", "description": "hover, focus, active, disabled → the styles that change in that state (the page's rules forced on the element).", "additionalProperties": { "$ref": "#/$defs/styles" } },
|
|
53
|
+
"asset": { "type": "object", "properties": { "kind": { "enum": ["img", "svg"] }, "src": { "type": "string" }, "size": { "type": "array" }, "alt": { "type": ["string", "null"] } } }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"used": { "type": "object", "description": "Token path → number of values that use it.", "additionalProperties": { "type": "integer" } },
|
|
58
|
+
"off": { "type": "array", "description": "Values outside the token set, most frequent first.", "items": { "type": "object", "properties": { "prop": { "type": "string" }, "value": { "type": "string" }, "count": { "type": "integer" }, "nearest": { "type": ["string", "null"] }, "sel": { "type": "string" } } } }
|
|
59
|
+
}
|
|
60
|
+
}
|
package/skills/kaloko/SKILL.md
CHANGED
|
@@ -11,7 +11,9 @@ description: >
|
|
|
11
11
|
reviewer rejected on a canvas, what is waiting in the Kaloko inbox, or how
|
|
12
12
|
to report a run in an issue, and when designing screens of a flow in a
|
|
13
13
|
design environment (live HTML steps, drafts, versions, the design
|
|
14
|
-
reference, checking implementation against it)
|
|
14
|
+
reference, checking implementation against it), and when implementing a
|
|
15
|
+
screen that has an approved design (read the step spec, build with its
|
|
16
|
+
tokens, check drift).
|
|
15
17
|
---
|
|
16
18
|
|
|
17
19
|
# Kaloko
|
|
@@ -28,9 +30,14 @@ the canvas, storage and collaboration are the service.
|
|
|
28
30
|
`kaloko capture` run `kaloko evaluate`) and `kaloko build` ran; you report the summary line,
|
|
29
31
|
failing criteria with evidence, and the preview path (or shared URL).
|
|
30
32
|
- When the run was shared, its URL and the summary are in the issue or PR
|
|
31
|
-
the work belongs to,
|
|
32
|
-
|
|
33
|
+
the work belongs to, the person got the review-mode link with what waits
|
|
34
|
+
for them (`kaloko share` prints it; `kaloko review` repeats it), and you
|
|
35
|
+
read `kaloko feedback` and `kaloko inbox` before continuing on that task.
|
|
33
36
|
- Changes to intent are written back to `ACCEPTANCE.md` (change log row).
|
|
37
|
+
- When you implemented a step from an approved design: you read its spec
|
|
38
|
+
(`kaloko spec <step>`), walked the implementation with `packs: [design]`,
|
|
39
|
+
and `kaloko drift` is clean or the PR names each difference left for a
|
|
40
|
+
person to decide.
|
|
34
41
|
- On a design environment: the folder follows the bundle rules and
|
|
35
42
|
`kaloko design lint` shows no error, `kaloko walk` and `kaloko sync` ran,
|
|
36
43
|
`kaloko status` shows no conflict, and you read the draft's feedback; a
|
|
@@ -56,12 +63,17 @@ a step in a sync conflict (`--mine`), turning on `html_view`, a real clean-up
|
|
|
56
63
|
(`kaloko prune --yes`), trashing someone else's run, archiving a scenario, and
|
|
57
64
|
anything the environment profile blocks. Never trash an accepted run, a
|
|
58
65
|
baseline, a run with review comments, or a run linked from an open PR or issue
|
|
59
|
-
comment.
|
|
66
|
+
comment. Never approve a
|
|
67
|
+
step or accept a run on a person's behalf; report what waits for them
|
|
68
|
+
(`kaloko review`, MCP `get_review_queue`) instead. Ask the person
|
|
60
69
|
for secret values (passwords, tokens, gate credentials) and for sign-ins only
|
|
61
70
|
they can do; never write secrets into the config, a scenario or a script.
|
|
62
71
|
|
|
63
72
|
## Where to look
|
|
64
73
|
|
|
74
|
+
- A new project or an organization without runs of its own (install, sign in,
|
|
75
|
+
the sample run, the first flow from a URL or a task, share, point the person
|
|
76
|
+
to the canvas): [reference/first-run.md](reference/first-run.md)
|
|
65
77
|
- Writing or updating the plan and scenario: [reference/acceptance.md](reference/acceptance.md)
|
|
66
78
|
- Android, iOS (Simulator), Electron and macOS apps, or screens captured by
|
|
67
79
|
another tool (Maestro, Appium, XCUITest) with their UI tree:
|
|
@@ -71,12 +83,14 @@ they can do; never write secrets into the config, a scenario or a script.
|
|
|
71
83
|
[reference/stacks.md](reference/stacks.md)
|
|
72
84
|
- Choosing playwright, agent or manual per step, and driving the shared
|
|
73
85
|
browser with your capabilities: [reference/execution.md](reference/execution.md)
|
|
74
|
-
- Evaluation,
|
|
86
|
+
- Evaluation, thresholds, the vision evaluator and its reasons, reading
|
|
87
|
+
results: [reference/evaluation.md](reference/evaluation.md)
|
|
75
88
|
- Environments, access (gates, accounts, sign-in), e-mails, readonly and
|
|
76
89
|
production rules, and what is yours to arrange for the project's stack:
|
|
77
90
|
[reference/environments.md](reference/environments.md)
|
|
78
|
-
- Sharing,
|
|
79
|
-
|
|
91
|
+
- Sharing, review mode and approvals carried over unchanged steps, reviewer
|
|
92
|
+
feedback, mentions, inbox, verdicts and baselines, and cleaning up runs
|
|
93
|
+
(trash, prune, keep, archive):
|
|
80
94
|
[reference/collaboration.md](reference/collaboration.md)
|
|
81
95
|
- Reading runs, feedback and the inbox over MCP instead of the CLI:
|
|
82
96
|
[reference/mcp.md](reference/mcp.md)
|
|
@@ -87,6 +101,9 @@ they can do; never write secrets into the config, a scenario or a script.
|
|
|
87
101
|
references, handoff, tokens, the design pack, canvas edits, branches, HTML
|
|
88
102
|
view, presenting the prototype, exports, bringing your own design skill):
|
|
89
103
|
[reference/design.md](reference/design.md)
|
|
104
|
+
- Implementing a screen from the approved design: read the step spec, build
|
|
105
|
+
with its tokens, walk, check drift, fix:
|
|
106
|
+
[reference/design.md#implement-from-the-approved-design](reference/design.md#implement-from-the-approved-design)
|
|
90
107
|
- Writing design pages that work on the live step, from disk and in other
|
|
91
108
|
tools (relative URLs, no CDNs, state in the URL, deterministic content,
|
|
92
109
|
links between steps; what `kaloko design lint` checks):
|
|
@@ -40,8 +40,21 @@ for public pages and landings; task flows keep their own criteria.
|
|
|
40
40
|
|
|
41
41
|
- `a11y` (`AC3xx`): images with alt, links and buttons with a name, labelled
|
|
42
42
|
fields, heading levels not skipped, WCAG AA text contrast, a `main`
|
|
43
|
-
landmark, unique ids
|
|
44
|
-
|
|
43
|
+
landmark, unique ids (`AC301`–`AC307`, read from the page). After the
|
|
44
|
+
screenshot the walk also presses Tab through the page: `AC308` tab order
|
|
45
|
+
(hidden focus, big jumps back, positive `tabindex`, controls left out or
|
|
46
|
+
mouse-only), `AC309` visible focus with at least 3 : 1 contrast (each stop
|
|
47
|
+
screenshotted focused and unfocused), `AC310` no keyboard trap (a modal
|
|
48
|
+
dialog may keep focus), `AC311` a skip link or main content within three
|
|
49
|
+
stops, `AC312` names from Chrome's accessibility tree (what a screen
|
|
50
|
+
reader announces). `AC313` landmarks (one `main`, labelled repeats) and
|
|
51
|
+
`AC314` ARIA (unknown roles and attributes, references to missing ids,
|
|
52
|
+
`aria-hidden` over focusable content) are read from the page. The first
|
|
53
|
+
five focus stops are kept as screenshots and the canvas can draw the tab
|
|
54
|
+
order on the capture. The walk only moves focus: nothing is clicked or
|
|
55
|
+
typed, and the page is left unfocused at the top. Stack steps walk the
|
|
56
|
+
keyboard on their first five pages. Still a subset of WCAG 2.2 AA; it
|
|
57
|
+
does not replace an audit with a screen reader.
|
|
45
58
|
- `perf` (`AC4xx`): LCP ≤ 2.5 s, CLS ≤ 0.1, TTFB ≤ 800 ms, page weight ≤ 3 MB,
|
|
46
59
|
measured in this walk's browser since the page load (lab values: the
|
|
47
60
|
machine and network of the walk matter; compare runs, not absolutes).
|
|
@@ -227,3 +240,22 @@ skips a session's nodes there. The canvas shows a chip with the person on
|
|
|
227
240
|
every step, groups the step list by person and draws hand-offs between
|
|
228
241
|
people as dashed arrows. Sessions run one after another, never in parallel,
|
|
229
242
|
and never share cookies.
|
|
243
|
+
|
|
244
|
+
## Ignore regions
|
|
245
|
+
|
|
246
|
+
Clocks, ads, carousels and "3 minutes ago" change on every capture and make
|
|
247
|
+
pixel diffs noisy. List them on the step and both the canvas diff and the
|
|
248
|
+
design pixel check (`AC801`) leave them out:
|
|
249
|
+
|
|
250
|
+
```yaml
|
|
251
|
+
- id: home
|
|
252
|
+
ignore:
|
|
253
|
+
- { selector: "#clock" } # every visible match
|
|
254
|
+
- { selector: ".ad-slot", viewports: [desktop] }
|
|
255
|
+
- { rect: [0, 620, 1280, 240], viewport: desktop } # page pixels of one viewport
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Do not guess them: `kaloko walk --stability 3` notes what moves while the
|
|
259
|
+
page sits still, and `kaloko stability` compares the step with earlier runs
|
|
260
|
+
and writes suggestions (see [execution.md](execution.md)). Ignore only noise;
|
|
261
|
+
a region that matters to a criterion stays in.
|
|
@@ -4,7 +4,10 @@ A run is only worth something once the people who decide have seen it. The
|
|
|
4
4
|
loop teams use:
|
|
5
5
|
|
|
6
6
|
1. **Share the run** (`kaloko share`, tester token, after asking). The
|
|
7
|
-
command prints the canvas URL
|
|
7
|
+
command prints the canvas URL and one line on what waits for a person,
|
|
8
|
+
e.g. `12 steps unchanged, approvals carried over; 5 need review: <link>`.
|
|
9
|
+
Give the person that review-mode link (it ends in `#review`) with the
|
|
10
|
+
counts, not just the canvas.
|
|
8
11
|
2. **Put the link where the work is tracked.** When the branch has a pull
|
|
9
12
|
request and `gh` is signed in, `kaloko share --pr` posts one comment per
|
|
10
13
|
scenario on it (summary line, canvas link, criteria that need attention
|
|
@@ -19,7 +22,10 @@ loop teams use:
|
|
|
19
22
|
that waits for review and turns green or red with the verdict; do not
|
|
20
23
|
report it as passed before a person accepted the run.
|
|
21
24
|
3. **Read feedback before continuing.** `kaloko feedback` lists the run
|
|
22
|
-
verdict,
|
|
25
|
+
verdict, returned steps first (with the reviewer's note), open comments,
|
|
26
|
+
failed manual criteria and how many steps still wait for a person.
|
|
27
|
+
`kaloko review` (or MCP `get_review_queue`) says exactly what a person
|
|
28
|
+
still has to decide; see *Review mode* below.
|
|
23
29
|
`kaloko inbox` lists what needs your attention across runs: mentions,
|
|
24
30
|
returned runs, rejected steps in your runs, new runs of scenarios you
|
|
25
31
|
reviewed, and expiry warnings. Both take `--json`; `kaloko inbox --read`
|
|
@@ -31,6 +37,34 @@ loop teams use:
|
|
|
31
37
|
reply in the tracker with the new link. The reviewer who returned it is
|
|
32
38
|
told about the new run automatically.
|
|
33
39
|
|
|
40
|
+
## Review mode
|
|
41
|
+
|
|
42
|
+
Reviewers decide in **review mode** on the canvas (`Q`, the Review button,
|
|
43
|
+
or a link ending in `#review`): one step at a time, the steps that need a
|
|
44
|
+
person first (failed, returned, the AI was unsure or did not evaluate,
|
|
45
|
+
changed since the last accepted run, open comments), with the accepted run
|
|
46
|
+
beside it. `J`/`K` move, `A` approves, `R` returns with a note, `C`
|
|
47
|
+
comments; at the end they accept or return the run.
|
|
48
|
+
|
|
49
|
+
- **Approvals carry over unchanged steps.** When a new run is shared, a step
|
|
50
|
+
whose screenshots are byte-identical to the last accepted run, whose
|
|
51
|
+
criteria are the same and whose deterministic checks pass keeps the
|
|
52
|
+
approval a person gave it there, with its origin (who, when, which run).
|
|
53
|
+
Only changed or failing steps go to the queue. Anyone can revoke a carried
|
|
54
|
+
approval; the step then waits again. Organizations on Business can turn
|
|
55
|
+
this off (Settings → Approvals).
|
|
56
|
+
- A carried approval never accepts the run and never counts for required
|
|
57
|
+
approvers: the run verdict is always a person's decision.
|
|
58
|
+
- **Report what waits**, do not decide it: run `kaloko review` (add `--json`
|
|
59
|
+
for the data, `--run <id>` for another run) or MCP `get_review_queue` and
|
|
60
|
+
tell the person how many steps wait and give them `review_url`.
|
|
61
|
+
- **Never approve or accept on a person's behalf.** Decisions by tokens and
|
|
62
|
+
agents (MCP `decide_step`, `add_comment` with `approved`) are recorded as
|
|
63
|
+
yours, shown apart, and do not count as a person's unless the organization
|
|
64
|
+
allows it. Return a step only when you found a real problem, with a note.
|
|
65
|
+
- Returned steps are feedback: fix them, share a new run, and resolve the
|
|
66
|
+
thread with what changed (see below).
|
|
67
|
+
|
|
34
68
|
## Processing feedback
|
|
35
69
|
|
|
36
70
|
Comments are threads in three places:
|
|
@@ -195,6 +229,15 @@ Settings → Storage (keep the newest N per scenario and environment).
|
|
|
195
229
|
Everything above is available over the API with an organization token:
|
|
196
230
|
|
|
197
231
|
- `GET /api/v1/orgs/<org>/feedback?run=<id|latest>[&scenario=<id>]`
|
|
232
|
+
- `GET /api/v1/orgs/<org>/runs/<id>/review-queue` (or
|
|
233
|
+
`GET /api/v1/orgs/<org>/review-queue?run=<id|latest>&scenario=<id>`):
|
|
234
|
+
`items` in review order with `reasons`, `decision` (approved, returned or
|
|
235
|
+
carried, with who and how), `unconfirmed` (an agent's or token's decision
|
|
236
|
+
that does not count), `changed`, criteria with evidence, `screenshots` and
|
|
237
|
+
`ref_screenshots`; `carried` with provenance; `counts` (`queue`,
|
|
238
|
+
`decided`, `pending`, `carried`, `clear`); `reference` (the accepted run);
|
|
239
|
+
`review_url`. Decisions use the comment and verdict routes;
|
|
240
|
+
`POST …/runs/<id>/carried/<step>/revoke` revokes a carried approval.
|
|
198
241
|
- `GET|POST /api/v1/orgs/<org>/runs/<id>/comments` (a step: `node_id`;
|
|
199
242
|
the whole run: `node_id: ""`), `GET|POST …/scenarios/<id>/comments`
|
|
200
243
|
(the whole flow), and per comment `PATCH`, `DELETE`, `POST …/<cid>/resolve`
|
|
@@ -39,8 +39,10 @@ design/
|
|
|
39
39
|
the bundle.
|
|
40
40
|
- Links between steps are plain relative links to the page: `<a href="../payment/index.html">`. The canvas
|
|
41
41
|
follows them; `data-kaloko-go="payment"` (or `data-qawalk-go`) moves to that step whatever the `href` says.
|
|
42
|
-
- Reference token values as CSS custom properties
|
|
43
|
-
check see
|
|
42
|
+
- Reference token values as CSS custom properties named after the token path (`color.accent` →
|
|
43
|
+
`var(--color-accent)`), so Dev mode, the step spec and the token check see which token a value comes from.
|
|
44
|
+
Define interaction states in CSS (`:hover`, `:focus-visible`, `:active`, `:disabled`): Dev mode and the spec
|
|
45
|
+
read them from the stylesheet.
|
|
44
46
|
- Write pages by the portable HTML rules ([portable-html.md](portable-html.md)): relative URLs only, bundled
|
|
45
47
|
fonts, no CDNs, state in the URL instead of storage, deterministic content, viewport meta and `lang`.
|
|
46
48
|
`kaloko design lint` checks the folder; `kaloko sync` runs it first and stops on errors (things that break on
|
|
@@ -94,8 +96,39 @@ Development builds from the design reference:
|
|
|
94
96
|
- `kaloko export --live [--version n] --out design/` writes the live files into a folder.
|
|
95
97
|
- MCP `get_step` (scenario, step, `ref: design`) returns the file list, a signed entry URL, the token set, the
|
|
96
98
|
page structure and open comments; `list_step_changes` names the steps that changed since the last reference.
|
|
99
|
+
- MCP `get_step_spec` (or `kaloko spec <step>`) returns what to build, element by element, with tokens; see
|
|
100
|
+
[Implement from the approved design](#implement-from-the-approved-design).
|
|
97
101
|
- Translate to the project's stack with the project's own skills; keep the step ids and the token names.
|
|
98
102
|
|
|
103
|
+
## Implement from the approved design
|
|
104
|
+
|
|
105
|
+
Use this when you build or change a screen that has an approved design step (a design reference, or a version
|
|
106
|
+
the person names). The step spec says what was approved in the terms you write code in: elements in page order
|
|
107
|
+
with role, accessible name and selector, and their colors, typography, spacing, radius, shadow and layout, each
|
|
108
|
+
value with the DTCG token it comes from. People see the same data in the canvas: *Dev mode* (`D`) in the step
|
|
109
|
+
detail.
|
|
110
|
+
|
|
111
|
+
1. **Read the spec** of each step you build: `kaloko spec <step>` (default `--ref design`; `--ref v4`, `draft` or a
|
|
112
|
+
run id), or MCP `get_step_spec`. `--json` gives `qawalk.spec.v1` (`schemas/spec.v1.json`).
|
|
113
|
+
2. **Implement with the tokens.** A line such as `button "Pay" 120×44 bg #2e6bff=color.brand.primary · pad 12px
|
|
114
|
+
20px=space.3 space.5` means `background: var(--color-brand-primary); padding: var(--space-3) var(--space-5)`
|
|
115
|
+
(the custom property is the token path with dots as dashes) or the project's own token mechanism. `not a token;
|
|
116
|
+
nearest x` means the design itself used a raw value: ask the designer before you copy it, and say so in the PR.
|
|
117
|
+
`:hover`, `:focus`, `:active` and `:disabled` lines are states to build; `(light)` names the mode of a value.
|
|
118
|
+
Keep the roles and names: they are how the implementation is matched with the design.
|
|
119
|
+
3. **Walk it** with `packs: [design]` on the implementation steps; the capture then stores the implementation's
|
|
120
|
+
spec next to the screenshot.
|
|
121
|
+
4. **Check drift**: `kaloko drift [step]` compares the walked run with the design reference of the linked design
|
|
122
|
+
branch and exits 1 while something differs. A shared run: MCP `get_step_drift` (`run`: id or `latest`).
|
|
123
|
+
Findings name the element, the expected token and value, and what the page has; `typed raw` means the value
|
|
124
|
+
is right but should come from the token; `not found` means the design's element (by role and name) is missing.
|
|
125
|
+
5. **Fix and repeat** until drift is clean or what is left is a decision for a person (a missing element on
|
|
126
|
+
purpose, a raw value the designer accepted); report those in the PR instead of hiding them.
|
|
127
|
+
|
|
128
|
+
Specs exist for captures made with the current CLI: walk the design and sync again if `kaloko spec` says there is
|
|
129
|
+
none. The pixel, structure and token checks of the design pack (`AC801`–`AC803`) still run in `kaloko evaluate`;
|
|
130
|
+
drift is the per-element view an agent can act on.
|
|
131
|
+
|
|
99
132
|
## Your data is yours
|
|
100
133
|
|
|
101
134
|
Everything exports in open formats, readable without Kaloko:
|
|
@@ -10,9 +10,11 @@ answer, so running it again is cheap.
|
|
|
10
10
|
- deterministic results computed in the live page at capture time (with
|
|
11
11
|
evidence: selector snippet, measured value);
|
|
12
12
|
- the semantic evaluator for semantic criteria. It is a replaceable provider;
|
|
13
|
-
the default is JEV by TypeSafe (`TYPESAFE_API_KEY`), which
|
|
14
|
-
|
|
15
|
-
`
|
|
13
|
+
the default is JEV by TypeSafe (`TYPESAFE_API_KEY`), which reads the
|
|
14
|
+
reduced page state (no images) and returns only a probability per question
|
|
15
|
+
(`score`, 0–1). Verdicts: `pass` ≥ 0.85, `fail` ≤ 0.15, otherwise
|
|
16
|
+
`needs_review` (thresholds in config);
|
|
17
|
+
- a vision evaluator for a second look with the screenshot (below);
|
|
16
18
|
- `manual` criteria stay `not_evaluated` until a reviewer decides.
|
|
17
19
|
|
|
18
20
|
Where the evaluator runs: with `TYPESAFE_API_KEY` set locally, your own key; without it, the organization's
|
|
@@ -25,6 +27,47 @@ report with the reason `kaloko evaluate` gives.
|
|
|
25
27
|
The default evaluator is text-only and most accurate in English. It is weak
|
|
26
28
|
at numbers, which is why those are checked deterministically.
|
|
27
29
|
|
|
30
|
+
## Vision and reasons
|
|
31
|
+
|
|
32
|
+
Every semantic verdict carries one sentence, `reason`, shown as "Why" next to
|
|
33
|
+
the score on the canvas, in `kaloko build`, in the walk summary and in
|
|
34
|
+
`get_run_summary`. With only the text evaluator the sentence says what it
|
|
35
|
+
read and how sure it was (JEV gives no explanation). The vision evaluator
|
|
36
|
+
gives a real one: it sees the screenshot, or a crop of the criterion's
|
|
37
|
+
`scope` (or `highlight`), and answers with a probability and what in the
|
|
38
|
+
image decided it. It is asked for:
|
|
39
|
+
|
|
40
|
+
- criteria with `vision: true` on the check (layout, imagery, "looks like the
|
|
41
|
+
brand", anything the text cannot show);
|
|
42
|
+
- text answers in the uncertain band (0.15–0.85 by default), which would
|
|
43
|
+
otherwise go to a person.
|
|
44
|
+
|
|
45
|
+
Its answer replaces the score (the text score stays as `text_score` in the
|
|
46
|
+
evaluation). Cost stays bounded: only those criteria, one request per
|
|
47
|
+
capture, images scaled to 1000 px wide and at most two tiles of a full page,
|
|
48
|
+
at most 40 uncached requests per evaluation, and answers cached by content
|
|
49
|
+
like the text ones. The provider is Anthropic's Messages API
|
|
50
|
+
(`ANTHROPIC_API_KEY`, model from `evaluators.vision.model`, default
|
|
51
|
+
`claude-opus-5-5`). Without the key it goes through the organization's
|
|
52
|
+
service, where one vision question counts as five of the monthly allowance;
|
|
53
|
+
when the text evaluator runs with your own `TYPESAFE_API_KEY`, the vision
|
|
54
|
+
evaluator also waits for your own key (`evaluators.vision.hosted: true`
|
|
55
|
+
lets it use the service anyway).
|
|
56
|
+
Configure or switch it off in `kaloko.config.yml`:
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
evaluators:
|
|
60
|
+
vision:
|
|
61
|
+
when: uncertain # flagged (only vision: true) | always | off
|
|
62
|
+
uncertain: [0.15, 0.85]
|
|
63
|
+
max_calls: 40
|
|
64
|
+
reason_language: en # cs for Czech reviewers
|
|
65
|
+
# vision: false # text evaluator only
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Report the reason with each `needs_review` or failing criterion; when the
|
|
69
|
+
evaluator saw the screenshot, say so.
|
|
70
|
+
|
|
28
71
|
When a criterion comes back `needs_review`, read the reduced state the
|
|
29
72
|
evaluator saw (`steps/<node>/<locale>/<viewport>.capture.json`, key `state`)
|
|
30
73
|
and the screenshot. Either the page is genuinely unclear (report it), the
|
|
@@ -61,3 +104,8 @@ it was not evaluated.
|
|
|
61
104
|
|
|
62
105
|
`needs_review` is not a failure: look at the screenshot and state your
|
|
63
106
|
reading, or leave it to the reviewer.
|
|
107
|
+
|
|
108
|
+
After a share, steps that did not change since the last accepted run keep a
|
|
109
|
+
person's approval; only the rest waits in review mode. Report it as the
|
|
110
|
+
share prints it ("12 steps unchanged, approvals carried over; 5 need review:
|
|
111
|
+
<link>") and never call a carried or agent approval a person's acceptance.
|
|
@@ -73,3 +73,21 @@ The environment policy and gate apply to every context the CLI creates (the
|
|
|
73
73
|
shared browser, `walk` sessions, one-off captures): blocked paths and, when
|
|
74
74
|
readonly, non-GET requests outside `allowed_post_paths` fail and are listed as
|
|
75
75
|
blocked requests in the capture; the gate's credentials go to its origins only.
|
|
76
|
+
|
|
77
|
+
## Noisy regions (`--stability`, `kaloko stability`)
|
|
78
|
+
|
|
79
|
+
When pixel diffs between runs keep flagging the same clock, ad or carousel:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
kaloko walk --stability 3 # 3 screenshots per capture, a moment apart; what moved is noted
|
|
83
|
+
kaloko stability # this run against the 3 previous local runs of the scenario
|
|
84
|
+
kaloko stability --apply # adds the suggested ignore: to each step (block-style YAML only)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`kaloko stability` writes `stability.json` and `stability.yml` into the run
|
|
88
|
+
and prints the suggestions per step: a selector when an element of the stored
|
|
89
|
+
page covers the region, otherwise a rectangle for one viewport. Look at the
|
|
90
|
+
screenshots before applying; a region that changed because the page really
|
|
91
|
+
changed is not noise. `--same-commit` compares only runs of the same commit,
|
|
92
|
+
`--runs a,b` names them, `--last N` sets how many runs are compared. Capture
|
|
93
|
+
the steps again after applying: the capture records the ignored regions.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# First run in a new project
|
|
2
|
+
|
|
3
|
+
Use this when the project has no `kaloko.config.yml` yet, or the organization
|
|
4
|
+
has no shared run of its own. The goal is one real flow on the canvas, shared,
|
|
5
|
+
with a person who knows where to approve it.
|
|
6
|
+
|
|
7
|
+
## Where the organization stands
|
|
8
|
+
|
|
9
|
+
- `GET /api/v1/orgs/<org>/getting-started` (MCP tool `get_started`) answers
|
|
10
|
+
`stage` (`empty`, `sample`, `active`), the sample run's URL, how many real
|
|
11
|
+
runs exist, the next steps and a prompt to start from.
|
|
12
|
+
- Every new organization has a **sample run**: a tea-shop checkout in Czech
|
|
13
|
+
and English on desktop and mobile, with one failing step per language, one
|
|
14
|
+
overflow on the phone and one criterion waiting for a person. It does not
|
|
15
|
+
count toward the plan and expires after 30 days. `<service>/<org>/sample`
|
|
16
|
+
opens it; it carries a short guide for the person (open a step, approve,
|
|
17
|
+
return with a note, share).
|
|
18
|
+
- If the person has never reviewed a run, send them the sample link first:
|
|
19
|
+
they learn the canvas there, not on your first draft. When it is gone,
|
|
20
|
+
`POST /api/v1/orgs/<org>/sample` (MCP `add_sample_run`, needs an upload or
|
|
21
|
+
full token) adds it again. Do not delete it unless asked
|
|
22
|
+
(`DELETE /api/v1/orgs/<org>/sample`).
|
|
23
|
+
|
|
24
|
+
## From zero to a first shared run
|
|
25
|
+
|
|
26
|
+
1. `npm install --save-dev kaloko`, then
|
|
27
|
+
`npx kaloko init --agent claude --org <org>` (it prints the sample link
|
|
28
|
+
and these steps). Fill `environments` in `kaloko.config.yml` with the URL
|
|
29
|
+
the app runs on.
|
|
30
|
+
2. `npx kaloko login`: the person approves a token in the browser. Ask them
|
|
31
|
+
to; never create or paste tokens yourself.
|
|
32
|
+
3. `npx kaloko doctor` and fix what it marks ✕.
|
|
33
|
+
4. Draft the first scenario:
|
|
34
|
+
- public pages: `npx kaloko draft <url> [url…]` writes a read-only
|
|
35
|
+
production scenario and a plan; sharpen purposes and criteria;
|
|
36
|
+
- a task or the main flow of the app: write `ACCEPTANCE.md` and
|
|
37
|
+
`qa/scenario.yml` as in [acceptance.md](acceptance.md). Pick the flow a
|
|
38
|
+
person would show a client (sign-up, checkout, the main form), not every
|
|
39
|
+
page.
|
|
40
|
+
5. `npx kaloko validate qa/scenario.yml`, then
|
|
41
|
+
`npx kaloko start --scenario qa/scenario.yml --env local`, `kaloko walk`,
|
|
42
|
+
`kaloko build`. Fix scenario mechanics until every step has a capture.
|
|
43
|
+
6. Ask before `kaloko share` (see *Ask first* in SKILL.md). After sharing,
|
|
44
|
+
give the person the canvas URL and say what to look at: the failing
|
|
45
|
+
criteria with their evidence and the steps that need a decision.
|
|
46
|
+
7. Read `kaloko feedback` when they have looked; fix what they returned and
|
|
47
|
+
walk again.
|
|
48
|
+
|
|
49
|
+
Done means: the scenario validates, the run is shared, its URL and summary
|
|
50
|
+
line are in your reply (and in the issue or PR the work belongs to), and the
|
|
51
|
+
person knows whether the sample or this run is what they should open.
|