@asrunjs/asrun 0.2.0 → 0.5.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/.claude/commands/explore-case.md +18 -15
  2. package/.claude/commands/gen-spec.md +16 -13
  3. package/.claude/commands/heal-spec.md +26 -23
  4. package/.claude/commands/make-case.md +11 -8
  5. package/.claude/skills/exploring-cases/SKILL.md +146 -132
  6. package/.claude/skills/exploring-cases/references/open-questions.md +116 -112
  7. package/.claude/skills/exploring-cases/references/readiness.md +146 -137
  8. package/.claude/skills/exploring-cases/references/user-handoff.md +157 -147
  9. package/.claude/skills/generating-specs/SKILL.md +219 -207
  10. package/.claude/skills/generating-specs/references/conventions.md +148 -148
  11. package/.claude/skills/making-cases/SKILL.md +72 -64
  12. package/.claude/skills/making-cases/references/normalizing.md +39 -38
  13. package/.claude/skills/making-cases/references/writes-and-email.md +37 -36
  14. package/AGENTS.md +169 -154
  15. package/CLAUDE.md +2 -1
  16. package/README.md +221 -410
  17. package/dist/lib/build-artifacts.d.ts +8 -0
  18. package/dist/lib/build-artifacts.js +10 -0
  19. package/dist/lib/case-doc.d.ts +311 -0
  20. package/dist/lib/case-doc.js +1117 -0
  21. package/dist/lib/cases.d.ts +2 -15
  22. package/dist/lib/cases.js +5 -78
  23. package/dist/lib/command.d.ts +24 -0
  24. package/dist/lib/command.js +90 -0
  25. package/dist/lib/fixtures.js +2 -2
  26. package/dist/lib/login-card.d.ts +22 -0
  27. package/dist/lib/login-card.js +183 -0
  28. package/dist/lib/node-cli.d.ts +6 -2
  29. package/dist/lib/node-cli.js +16 -6
  30. package/dist/lib/profiles.d.ts +25 -1
  31. package/dist/lib/profiles.js +54 -1
  32. package/dist/lib/run-filters.js +10 -2
  33. package/dist/lib/session.d.ts +13 -1
  34. package/dist/lib/session.js +73 -7
  35. package/dist/lib/targets.d.ts +5 -13
  36. package/dist/lib/targets.js +4 -29
  37. package/dist/lib/values.js +2 -2
  38. package/dist/scripts/build.js +2 -1
  39. package/dist/scripts/check-cases.js +74 -6
  40. package/dist/scripts/doctor.js +115 -4
  41. package/dist/scripts/explore.js +22 -1
  42. package/dist/scripts/go.mjs +188 -188
  43. package/dist/scripts/init.d.ts +7 -1
  44. package/dist/scripts/init.js +62 -14
  45. package/dist/scripts/lint-specs.js +4 -1
  46. package/dist/scripts/login.js +30 -2
  47. package/dist/ui/server/actions.d.ts +4 -0
  48. package/dist/ui/server/actions.js +25 -12
  49. package/dist/ui/server/ai-models.d.ts +3 -0
  50. package/dist/ui/server/ai-models.js +347 -0
  51. package/dist/ui/server/ai-stream.d.ts +34 -9
  52. package/dist/ui/server/ai-stream.js +283 -77
  53. package/dist/ui/server/app.js +74 -12
  54. package/dist/ui/server/jobs.d.ts +7 -1
  55. package/dist/ui/server/jobs.js +69 -15
  56. package/dist/ui/server/profiles.d.ts +20 -1
  57. package/dist/ui/server/profiles.js +214 -38
  58. package/dist/ui/shared/ai-commands.d.ts +16 -6
  59. package/dist/ui/shared/ai-commands.js +35 -12
  60. package/dist/ui/shared/types.d.ts +114 -0
  61. package/docs/agents/case-format.md +193 -0
  62. package/docs/agents/framework-dev.md +167 -0
  63. package/docs/ai-workflow.md +235 -0
  64. package/docs/cases.md +189 -0
  65. package/docs/commands.md +175 -0
  66. package/docs/install.md +174 -0
  67. package/docs/profiles.md +139 -0
  68. package/docs/theme-guide.html +922 -692
  69. package/docs/troubleshooting.md +101 -0
  70. package/docs/web-ui.md +139 -0
  71. package/package.json +20 -1
  72. package/templates/.claude/commands/explore-case.md +22 -21
  73. package/templates/.claude/commands/gen-spec.md +21 -20
  74. package/templates/.claude/commands/heal-spec.md +31 -30
  75. package/templates/.claude/commands/make-case.md +13 -12
  76. package/templates/.mcp.json +8 -8
  77. package/templates/AGENTS.md +66 -45
  78. package/templates/cases/_template.md +57 -63
  79. package/templates/opencode.jsonc +16 -11
  80. package/templates/profiles.yaml +42 -29
  81. package/ui/dist/assets/{dist-D_I45xgJ.js → dist-BudNAjOv.js} +1 -1
  82. package/ui/dist/assets/{dist-CBV7_vDN.js → dist-C-eCSFja.js} +1 -1
  83. package/ui/dist/assets/{dist-gF2bFn-5.js → dist-CO4heSzh.js} +1 -1
  84. package/ui/dist/assets/{dist-DX8knLVr.js → dist-CPLhRzom.js} +1 -1
  85. package/ui/dist/assets/{dist-BqAROxzI.js → dist-DEPJZ3Me.js} +1 -1
  86. package/ui/dist/assets/{dist-BwYRbe5s.js → dist-DZ7RiqmA.js} +1 -1
  87. package/ui/dist/assets/{dist-BHaIOZdW.js → dist-LixzGniF.js} +1 -1
  88. package/ui/dist/assets/{dist-DHCY9KpV.js → dist-O_RVi4tP.js} +1 -1
  89. package/ui/dist/assets/{dist-BiE4suo9.js → dist-jPS6HCJ5.js} +1 -1
  90. package/ui/dist/assets/{dist-BSJMM_jp.js → dist-vmf3fnkM.js} +1 -1
  91. package/ui/dist/assets/index-BIGt8Hfz.js +338 -0
  92. package/ui/dist/assets/index-CTmnF0Wp.css +2 -0
  93. package/ui/dist/help/ai-tools.png +0 -0
  94. package/ui/dist/help/case-explore.png +0 -0
  95. package/ui/dist/help/case-generate.png +0 -0
  96. package/ui/dist/help/cases-memo.png +0 -0
  97. package/ui/dist/help/profile-form.png +0 -0
  98. package/ui/dist/help/profiles-login.png +0 -0
  99. package/ui/dist/help/reports.png +0 -0
  100. package/ui/dist/help/run-tests.png +0 -0
  101. package/ui/dist/index.html +22 -16
  102. package/ui/dist/assets/index-DmasQ6Rq.css +0 -2
  103. package/ui/dist/assets/index-I7uU93kk.js +0 -294
@@ -0,0 +1,235 @@
1
+ # The AI workflow
2
+
3
+ Four commands carry a test case from rough notes to a Playwright spec, and repair it when the product changes. They
4
+ work the same in Claude Code and OpenCode: each command is a thin shell that loads a skill from the framework package,
5
+ so the procedure lives in one place and a project can refine it (see
6
+ [Customizing the prompts](#customizing-the-prompts)).
7
+
8
+ | Command | What it does |
9
+ |---|---|
10
+ | `/make-case <notes-or-path>` | Turn raw notes into case documents. Optional step. |
11
+ | `/explore-case <case-id> <env> [--attended]` | Open the real pages and write what they show into the case. |
12
+ | `/gen-spec <case-id> <env> [--force]` | Write `tests/<case-id>.spec.ts` and fill the case's `## Values`. |
13
+ | `/heal-spec <case-id> <env> [--profiles a,b] [--report <run-id>] [--hints <file>]` | Diagnose a failing spec from its last report and fix it. |
14
+
15
+ The environment is **required** by every command except `/make-case`, which does not open pages. There is no default.
16
+
17
+ Every command ends with `RESULT: ready` or `RESULT: needs-input`. `needs-input` means somebody has to answer a
18
+ question in the case file (or, in the web UI, in the job's **Answer** sheet). A one-shot run cannot ask in the chat:
19
+ it writes its questions into the case and stops.
20
+
21
+ In the web UI you never type a command: choose the step in a case toolbar and press **Run AI**, or paste notes on
22
+ **Cases > From memo**. The UI runs the selected AI tool and streams the output into the Jobs bar.
23
+
24
+ ## 0. Optional: start from raw notes
25
+
26
+ ```
27
+ /make-case path/to/notes.md
28
+ ```
29
+
30
+ or paste the text after the command. The agent normalizes the notes into steps in the project's artifact language
31
+ (English by default), marks the steps that write data (`[writes]`) or send email (`[email]`), keeps the original lines
32
+ in a `## Source` table, and lists the gaps with a suggested default for each. It never invents expected values
33
+ (unknown ones become a sentence with a `TODO` block under it) and never opens a browser. What the pages really show
34
+ is found in step 2.
35
+
36
+ It writes `cases/<id>.md` only, and never overwrites an existing case. `pnpm case:check` validates the result.
37
+
38
+ ## 1. Write or open the case
39
+
40
+ `pnpm case:new header-logo` creates `cases/header-logo.md` from the template, or use a case you already have. The
41
+ format is in [cases.md](./cases.md). Write the steps, the expectations and — when you know them — the values.
42
+
43
+ ## 2. Explore the case (only where a person and the agent work together)
44
+
45
+ ```
46
+ /explore-case header-logo staging
47
+ ```
48
+
49
+ The agent opens the real pages of the profiles that are logged in and only **reads** them. It never creates, edits,
50
+ publishes, shares or deletes anything, in staging or in production. It:
51
+
52
+ - fixes wording in the case file and writes what the pages show into it, marked `observed, 🔍 to be confirmed by QA`;
53
+ - turns what it cannot know into `🚫 TODO [blocking]` / `❓ TODO [non-blocking]` questions, each under a sentence that
54
+ is its suggestion and can stand in the case as it is, with a last line `- ✍️ Answer:`;
55
+ - lists the candidates instead of guessing a target (`## Targets`);
56
+ - edits the case file only (plus reference screenshots under `.explore/targets/`).
57
+
58
+ **To accept the suggestion, delete the whole block.** **To answer, write after `Answer:`.** Then run the command
59
+ again. In the web UI, the run's **What to do next** bar offers **Answer**: the question list, an answer box,
60
+ **Use the suggested defaults** and **Generate spec**. A continued job keeps the same conversation and permissions, and
61
+ can be continued once per turn, only while the UI server has not restarted.
62
+
63
+ ### Attended exploration (`--attended`)
64
+
65
+ Use it for a case that creates or edits data, or needs a choice (an existing document, items). Stay at the computer:
66
+ the browser window opens and the agent waits while you do those steps.
67
+
68
+ - The window shows a small draggable card in the bottom-left corner with what to do, **Done** (hand control back) and
69
+ **Cannot do this**. The agent also continues by itself when the finish condition it named shows up (for example a
70
+ "Saved" message); the card turns green when control is back. It waits up to 10 minutes per step.
71
+ - What you did is recorded in the case under the step as **general recipe lines**
72
+ (`- Recipe (phase): ...`): positions instead of names of changeable data, `[asrun]` names for created data, control
73
+ labels but no selectors.
74
+ - Delete the `[asrun]` items you created before you press Done on the last step.
75
+ - Without `--attended` the agent never waits: a step that needs a person becomes a `TODO [blocking]`.
76
+
77
+ In the web UI the **Attended** box is ticked by default for Explore.
78
+
79
+ ## 3. Log in the profiles the case needs
80
+
81
+ ```bash
82
+ pnpm status
83
+ pnpm signin example-staging
84
+ ```
85
+
86
+ Skip this for a `login: none` case. Sessions are per profile; see [profiles.md](./profiles.md).
87
+
88
+ ## 4. Generate the spec
89
+
90
+ ```
91
+ /gen-spec header-logo staging
92
+ ```
93
+
94
+ The agent replays the case on the real pages with your saved sessions (it never writes data) and writes
95
+ `tests/header-logo.spec.ts`. It fills the case's `## Values` for the profiles it explored and checks the files
96
+ (`pnpm typecheck`, `pnpm case:check`; `pnpm spec:lint <case-id>` must have no failure).
97
+
98
+ It does **not** run the spec and does **not** create screenshot baselines: running is a separate step (6).
99
+
100
+ - A case must be in the project's artifact language. If it is in another language, `/gen-spec` translates the case
101
+ file first and tells you.
102
+ - A `[writes]` or `[input]` step with no `- Recipe:` line under it is not generated: the command stops with
103
+ `needs-input` and asks you to run step 2 with `--attended`.
104
+ - An existing spec is left alone; `--force` regenerates it from the case. The case's `## Values` and the baseline
105
+ images are kept, and the report lists the baselines that may be out of date. In the web UI, **Run** on a case that
106
+ has a spec asks "Overwrite the spec?" first.
107
+
108
+ ## 5. Review what the AI produced
109
+
110
+ Check that the spec reads like the case, and that `## Values` has the right values for each profile. The spec imports
111
+ `test`/`expect` from `@asrunjs/asrun/fixtures`, never from `@playwright/test`, and every step is a `test.step` named
112
+ after the case's step. You normally do not edit either file by hand, but you can.
113
+
114
+ ## 6. Run it and read the evidence
115
+
116
+ ```bash
117
+ pnpm asrun --env staging --grep header-logo
118
+ pnpm report
119
+ ```
120
+
121
+ The first run of a spec with visual checks writes its baselines. Run it once with `--update-snapshots`, then open
122
+ every new image under `tests/<case>.spec.ts-snapshots/` and confirm it shows the target card's element for that
123
+ profile. Baselines are the source of truth for pixel checks.
124
+
125
+ Then run all profiles and read the report: the video of each test, screenshots on failure, and the trace with the page
126
+ and the network requests. See [commands.md](./commands.md) for filters and baselines.
127
+
128
+ ## Healing a failing spec
129
+
130
+ ```
131
+ /heal-spec header-logo staging
132
+ ```
133
+
134
+ or **Heal spec** in the case toolbar, or **Heal this failure** on a failed result of the Reports page.
135
+
136
+ The agent decides whether the test needs updating or whether it found a real product problem. It **never** edits an
137
+ expectation just to make a failure disappear.
138
+
139
+ - The environment is required. Only the profiles that are logged in there run (`--logged-in` leaves the others out
140
+ instead of failing them).
141
+ - The web UI first opens a dialog: which profiles to heal (every logged-in profile ticked) and an optional text box
142
+ for what you want healed, with evidence or clues. The text is stored in `.tmp/hints/` and passed as `--hints`.
143
+ Clues guide the diagnosis; they never lower the bar for a product finding.
144
+ - It reads the existing report first:
145
+
146
+ ```bash
147
+ pnpm heal:evidence --case header-logo --env staging
148
+ ```
149
+
150
+ It prints status, failed step, error, call log and screenshots per profile, and whether the report is fresh or
151
+ stale. The case is run again only when there is no report, the report is stale, it does not explain the failure, or
152
+ a clue has to be checked. After editing the spec, it runs the case once to confirm.
153
+
154
+ ## Why a generated spec can be trusted
155
+
156
+ Three binding rules hold in exploring, generating and healing. They live in the framework's `AGENTS.md` and in
157
+ `.claude/skills/exploring-cases/references/assertion-targets.md` and `readiness.md`.
158
+
159
+ - **Targets.** A check on a logo, image, icon, file, PDF, screenshot or repeated text points to a `[T1]` card under
160
+ `## Targets`: what it is (and what it is not), where it is, how it is identified (never `.first()` or a bare `img`),
161
+ when it is ready, and a highlighted reference screenshot under `.explore/targets/`. When in doubt the agent does not
162
+ pick: it writes a `🚫 TODO [blocking]` that names the exact image, text, place or UI, with the candidates listed.
163
+ - **Readiness.** After an action that loads something, the key requests must finish and the content must be visible
164
+ before anything is read. Specs use `actAndWait` (`@asrunjs/asrun/ready`); "missing" and "empty" checks need a
165
+ positive ready signal first. Healing treats a "missing / empty / timeout" failure as a readiness problem first.
166
+ - **Downloads.** `verifyDownload` (`@asrunjs/asrun/downloads`) checks that the download starts, finishes, the file
167
+ exists and the file is valid for its kind. Any failure or timeout fails the whole step with a message that names the
168
+ stage.
169
+ - **PDFs are read like a person reads them.** `expectPdf` (`@asrunjs/asrun/pdf`) renders the pages to images (a logo
170
+ region must not be blank and must match the profile's baseline, 2% tolerance) and extracts the text. Every page is
171
+ checked unless the case limits the pages in strong words ("only", "exactly", "must"); "should", "always", "usually"
172
+ or no wording mean every page.
173
+
174
+ Checks you can run yourself:
175
+
176
+ | Command | What it checks |
177
+ |---|---|
178
+ | `pnpm spec:lint [case-id]` | fails on screenshots without a target, `.first()` on images, sleeps, `networkidle`, raw download events, a `%PDF` check on its own |
179
+ | `pnpm baselines:review [case-id] [--new]` | lists every baseline with the card that says what it must show, so you can look at each new image |
180
+ | `pnpm case:check [case-id]` | fails on an incomplete card or bad values; warns on an image check with no card |
181
+ | `pnpm pdf:render <file.pdf> --out <dir> [--pages all] [--corner top-left]` | renders the pages to images and prints the text, to look at a PDF by hand |
182
+
183
+ ## Customizing the prompts
184
+
185
+ The commands are shells; the procedure is a skill in the framework package:
186
+
187
+ - commands: `.claude/commands/*.md` (Claude Code) and `.opencode/commands/*.md` (OpenCode);
188
+ - skills: `<framework>/.claude/skills/{making-cases,exploring-cases,generating-specs}/`.
189
+
190
+ A project refines a skill with a **flat override file**, named after the skill (not the command):
191
+
192
+ ```
193
+ .claude/skills/generating-specs.md
194
+ .claude/skills/exploring-cases.md
195
+ .claude/skills/making-cases.md
196
+ ```
197
+
198
+ The command and the skill read it; it is not discovered as a standalone skill. Rules combine in this order, and a
199
+ later one wins where they conflict:
200
+
201
+ 1. `F1`–`F6` of the framework baseline: safety, in every project. Nothing can change them.
202
+ 2. The framework baseline, `<framework>/AGENTS.md`.
203
+ 3. The framework skill.
204
+ 4. The project override `.claude/skills/<skill>.md`.
205
+ 5. The project's own `AGENTS.md` §2 (Project rules).
206
+
207
+ An override is plain Markdown; write only what differs:
208
+
209
+ ```markdown
210
+ # generating-specs in this project
211
+
212
+ - Step titles in `test.step` keep the case's wording, including the number.
213
+ - A helper used by more than one spec goes in `tests/helpers/<name>.ts`.
214
+ - The staging account has no published documents: assert the empty state there instead.
215
+ ```
216
+
217
+ The project's `AGENTS.md` §2 is the other place: a table of rule ids (`F7`–`F10`; `F1`–`F6` are fixed) with `narrows`
218
+ (stricter) or `replaces` (a different default). For example, a project that writes cases in Simplified Chinese
219
+ `replaces` `F10`. `pnpm asrun doctor` fails on an unknown id, a missing `Effect`, or a non-negotiable rule, and lists
220
+ the overrides it sees.
221
+
222
+ An update never overwrites `AGENTS.md`, `CLAUDE.md` or `.claude/skills/**`: `pnpm asrun init --force` refuses them
223
+ without `--reset-rules`. Never edit the framework package itself.
224
+
225
+ Project-level AI tool configuration:
226
+
227
+ | File | What it sets |
228
+ |---|---|
229
+ | `.mcp.json` | the `playwright-test` MCP server for Claude Code |
230
+ | `opencode.jsonc` | the same MCP server for OpenCode, plus an optional `"model"` for one-shot runs |
231
+ | `.claude/settings.json` | the sandbox exceptions for `pnpm asrun`, `pnpm status`, `pnpm heal:evidence` |
232
+
233
+ In the web UI, the top bar chooses the AI tool and the model for every AI run, and **Thinking** passes `--thinking`
234
+ to OpenCode. The choice is remembered per CLI in the browser. Without a model, a one-shot OpenCode run uses its
235
+ built-in free model, which has a daily usage limit.
package/docs/cases.md ADDED
@@ -0,0 +1,189 @@
1
+ # Writing case documents
2
+
3
+ A case is a Markdown file that a person writes and reviews. It is the source of truth for the test: steps,
4
+ expectations, the exact values and the visual targets. The spec is generated from it and never replaces it.
5
+
6
+ ## Where cases live
7
+
8
+ - `cases/<name>.md`, or one group folder: `cases/<group>/<name>.md`.
9
+ - The **id** is the path below `cases/` without `.md`: `header-logo` or `documents/landing-page`. Names are lowercase
10
+ words with single dashes.
11
+ - The spec of a case lives at the same path below `tests/` (`tests/documents/landing-page.spec.ts`), with its
12
+ `*-snapshots/` folder next to it.
13
+ - Files starting with `_` are ignored (`cases/_template.md` is the template).
14
+
15
+ ```bash
16
+ pnpm case:new header-logo # creates cases/header-logo.md from the template
17
+ pnpm case:new documents/header-logo # also creates the group folder
18
+ pnpm case:check # validates every case (or: pnpm case:check <id>)
19
+ ```
20
+
21
+ ## The document
22
+
23
+ ````markdown
24
+ ---
25
+ id: header-logo
26
+ title: The header shows the product logo
27
+ login: required
28
+ suites: [smoke, regression]
29
+ ---
30
+
31
+ ## Goal
32
+ Why this case exists, in one or two sentences.
33
+
34
+ ## Steps
35
+ 1. Open `/`.
36
+ - Ready when: the header has rendered its menu.
37
+ 2. The header shows the product logo. [T1]
38
+
39
+ ## Expected
40
+ - The logo is the product logo for this profile, not a customer or partner logo.
41
+
42
+ ## Values
43
+ ```yaml
44
+ default:
45
+ logoAlt: Acme
46
+ example-staging:
47
+ logoAlt: Acme Staging
48
+ ```
49
+
50
+ ## Visual checks
51
+ - The header logo matches its baseline. [T1]
52
+
53
+ ## Targets
54
+ ### T1 Header logo
55
+ - What: the product logo, not the customer logo in the banner.
56
+ - Where: top-left of the header.
57
+ - Identified by: role `img`, name "Acme".
58
+ - Not: the customer logo in the banner and the favicon.
59
+ - Ready when: the image has a non-zero natural width.
60
+ - Reference example-staging: .explore/targets/header-logo/T1-example-staging.png
61
+
62
+ ## Notes
63
+ Anything a reviewer should know.
64
+ ````
65
+
66
+ The headings are fixed English tokens (`Goal`, `Steps`, `Expected`, `Values`, `Visual checks`, `Targets`, `Notes`,
67
+ and `Source` when the case was made from raw notes). A project language never translates them.
68
+
69
+ ## Front matter
70
+
71
+ | Key | Meaning |
72
+ |---|---|
73
+ | `id` | The case id (the path without `.md`). |
74
+ | `title` | One-line summary, shown in the editor and the spec. |
75
+ | `login` | `required` (the default) or `none` for a public page that needs no saved session. A `login: none` spec sets `test.use({mustLogin: false})`. |
76
+ | `suites` | Required. One or more lowercase words: `suites: [smoke, regression]`. A case runs in every suite it lists. |
77
+ | `writes` | The environments in which the case may create or change data, for example `writes: [staging]`. Omit it for a read-only case. |
78
+ | `writes-note` | Required with `writes`: what the case creates or changes, and that it is removed again. |
79
+ | `envs` | Optional: run the case only in these environments; elsewhere it is skipped. Must include every environment in `writes`. |
80
+
81
+ `priority: <level>` is the old name of `suites`; it is read as `suites: [<level>]`. Do not use both.
82
+
83
+ ## Steps
84
+
85
+ Numbered lines. Keep them short and in plain language: the spec uses each step's text verbatim in `test.step`, so the
86
+ report and the trace read like the case.
87
+
88
+ - **`- Ready when:`** under a step says what positive signal shows that what the step loads is really there (the list
89
+ has its first row, the editor is visible). The generated spec waits for it before it reads anything. It is not an
90
+ assertion; what must be true goes under `## Expected`.
91
+ - **Step tags**: `[writes]` for a step that creates or changes data, `[input]` for a step that needs a person's
92
+ choice (an existing document, items), `[email]` for a step that sends email. They are written at the end of the
93
+ step line.
94
+ - **`- Recipe (phase):`** under a tagged step records what a person did in the browser during an attended
95
+ exploration: positions instead of names of changeable data (`select items #1 to #3`), `[asrun]` names for created
96
+ data, control labels but no selectors. Several lines per step are normal. A tagged step with no recipe is not
97
+ generated: `/gen-spec` stops with `needs-input`.
98
+ - **Open questions** go in a `🚫 TODO [blocking]` or `❓ TODO [non-blocking]` block under the sentence they are about,
99
+ with the agent's suggestion written above and a last line `- ✍️ Answer:`. Accepting a question means deleting the
100
+ whole block; answering means writing after `Answer:`. The marker `🔍 to be confirmed by QA` / `👁️ not observed`
101
+ marks something seen but not settled.
102
+
103
+ ## Expected
104
+
105
+ Bullets: behaviour that must hold for every profile. Profile differences belong in `## Values`, not in prose.
106
+
107
+ ## Values
108
+
109
+ The exact literals the spec asserts, in one fenced `yaml` block: names, labels, options, test data names.
110
+
111
+ ```yaml
112
+ default:
113
+ createButton: Create New
114
+ emptyList: No documents yet
115
+ example-staging:
116
+ createButton: Create a document
117
+ ```
118
+
119
+ - `default` applies to every profile; an entry named after a profile id is **deep-merged over it**. An empty cell in
120
+ the UI removes the key from that profile.
121
+ - Only profile ids from `profiles.yaml` (or `default`) may appear at the top level. An unknown entry is an error.
122
+ - The case document is the only place where expected values live. `profiles.yaml` holds connection settings only.
123
+ - A profile with no entry (and no `default`) fails with "Missing values"; an empty value or one that starts with `TODO`
124
+ fails with "Incomplete values". Both messages say what to fill in.
125
+ - Specs read them with `requireValues('<id>', profile)` and never parse the YAML themselves.
126
+
127
+ ## Visual checks and targets
128
+
129
+ A visual check names the element that must match a screenshot baseline. A check on a logo, image, icon, file, PDF,
130
+ screenshot or repeated text refers to a card under `## Targets` as `[T1]`, and the spec carries `// target: T1` on the
131
+ line before the check.
132
+
133
+ Each card has these fields:
134
+
135
+ - `What`: the thing itself, and what it is **not** when a look-alike exists.
136
+ - `Where`: the region of the page, dialog, tab or file page.
137
+ - `Identified by`: role and name, test id, alt text or title; never a position.
138
+ - `Not`: the look-alikes that must not be matched.
139
+ - `Ready when`: what shows the content has loaded.
140
+ - `Reference <profile-id>:` a screenshot under `.explore/targets/<case-id>/`, or `none: <reason>`.
141
+
142
+ Do not guess a target: when it is unclear which element is meant, the exploring agent writes a `🚫 TODO [blocking]`
143
+ that names the candidates, and a person settles it. The binding rules are in
144
+ `../.claude/skills/exploring-cases/references/assertion-targets.md` of the framework package.
145
+
146
+ ## Data writing
147
+
148
+ By default a case is read-only: look, read, compare. No creating, editing, deleting, submitting or accepting
149
+ anything.
150
+
151
+ A case may write only in the environments listed under `writes`, and only data of the test account:
152
+
153
+ - everything it creates is named `[asrun] ...` (the `records` fixture refuses other names);
154
+ - created data is registered with `records.track` and removed after the test, also after a failure;
155
+ - the case's own last step is normally the deletion; `track` is the safety net;
156
+ - staging needs no confirmation. In any other environment a person confirms every run (typing the environment name,
157
+ or the dialog in the web UI). `--read-only` leaves the write cases out, and `--allow-writes=<env>` skips the prompt
158
+ for unattended runs (AI agents never use it);
159
+ - write cases are never retried and run one at a time per profile;
160
+ - a case that is not allowed to write in the current environment is **skipped with a notice**, not failed;
161
+ - whatever could not be removed is listed in red in the report and by `pnpm cleanup --env <env>`; delete it by hand,
162
+ then `--forget`.
163
+
164
+ Email is never sent in production, and elsewhere only when the profile has `email: true`. A step that sends email is
165
+ wrapped in the spec with `emailStepAllowed(profile, '<step title>')`, which records the skip in the report.
166
+
167
+ Never write a case for the login page or the login flow: login is a person's job (`pnpm signin`), not a test.
168
+
169
+ ## What `pnpm case:check` validates
170
+
171
+ It reads every case (or the ids you pass) and prints one line per case. It **fails** on:
172
+
173
+ - invalid front matter (missing `suites`, a bad `writes`/`envs` combination, `writes` without `writes-note`);
174
+ - a target card without its required fields (`What`, `Where`, `Identified by`), or a reference image outside
175
+ `.explore/targets/<case-id>/`;
176
+ - a `[T<n>]` reference with no card;
177
+ - an invalid `## Values` block (bad YAML, an unknown profile, a non-map entry).
178
+
179
+ It **warns** (usable, but something to settle) on: a reference image missing on this machine (`.explore/` is
180
+ git-ignored), a card without a reference line, a check on an image or file with no target card, no `## Values`, a
181
+ `[input]`/`[writes]` step with no `- Recipe:` line, the old `## Inputs` / `## Data` sections, and question blocks
182
+ that cannot be answered in place.
183
+
184
+ ## Rename or move a case
185
+
186
+ Use the Cases page (click the file name or the pencil icon). The spec and the baseline images move with it and the
187
+ spec is rewritten to match. By hand, move the three things together (`cases/...md`, `tests/...spec.ts`, its
188
+ `*-snapshots/`), and keep the spec's `// case:` line and its imports unchanged: specs import the framework by package
189
+ name, so their folder depth does not change the imports.
@@ -0,0 +1,175 @@
1
+ # Command reference
2
+
3
+ Every command runs in a workspace (the folder that owns `profiles.yaml`); see
4
+ [install.md](./install.md#which-workspace-a-command-runs-against). In a project the scripts of `package.json` are
5
+ thin wrappers, so `pnpm asrun`, `pnpm signin` and the rest work as below. The same commands through the package are
6
+ `pnpm exec asrun ...`.
7
+
8
+ | I want to... | Command |
9
+ |---|---|
10
+ | Install / update the framework | `pnpm dlx @asrunjs/asrun init --install`, `pnpm add -D @asrunjs/asrun@latest` |
11
+ | Prepare or check the workspace | `pnpm asrun init [--update]`, `pnpm asrun doctor` |
12
+ | Start the web UI | `pnpm go`, `pnpm ui`, `pnpm ui:dev` |
13
+ | See which profiles are logged in | `pnpm status [--env <env>]` |
14
+ | Log in a profile (a person, with 2FA) | `pnpm signin <profile-id>` |
15
+ | Run tests | `pnpm asrun --env <env> [filters]` |
16
+ | Open the last report | `pnpm report` |
17
+ | Look around a page as a profile | `pnpm explore <profile-id> [path]` |
18
+ | Read what the last report says about a case | `pnpm heal:evidence --case <case-id> --env <env>` |
19
+ | List or forget test data that could not be removed | `pnpm cleanup --env <env> [--forget]` |
20
+ | Create or validate a case | `pnpm case:new <id>`, `pnpm case:check [case-id]` |
21
+ | Check a spec | `pnpm spec:lint [case-id]`, `pnpm baselines:review [case-id] [--new]` |
22
+ | Look at a PDF | `pnpm pdf:render <file.pdf> --out <dir> [--pages all] [--corner top-left]` |
23
+ | Type-check the workspace | `pnpm typecheck` |
24
+
25
+ ## Running tests: `pnpm asrun`
26
+
27
+ ```bash
28
+ pnpm asrun --env staging
29
+ ```
30
+
31
+ `--env` is **required**; there is no default. A run selects the profiles whose `env` matches, checks their sessions,
32
+ runs the selected cases once per profile, and writes one report folder per run.
33
+
34
+ ```bash
35
+ pnpm asrun --env production --profile example-production
36
+ pnpm asrun --env production --skip-profile example-staging,example-production
37
+ pnpm asrun --env staging --tag us
38
+ pnpm asrun --env production --logged-in
39
+ pnpm asrun --env production --suite smoke
40
+ pnpm asrun --env production --suite smoke,regression
41
+ pnpm asrun --env production --grep landing
42
+ pnpm asrun --env production --read-only
43
+ pnpm asrun --env staging --ui
44
+ ```
45
+
46
+ | Flag | Effect |
47
+ |---|---|
48
+ | `--env <env>` | Required. The environment to run. |
49
+ | `--profile a,b` | Only these profiles. |
50
+ | `--skip-profile a,b` | Every profile except these (for example the ones that are not logged in). |
51
+ | `--tag <tag>` | Only the profiles that carry the tag (comma-separated for several). |
52
+ | `--logged-in` | Only the profiles whose saved session works; the others are left out instead of failing. |
53
+ | `--suite <name>` | Only the cases whose front matter lists the suite (comma-separated; a case in two chosen suites runs once). `--priority` is the old name. |
54
+ | `--grep <text>` | Only the cases whose title matches (Playwright `--grep`). |
55
+ | `--read-only` | Leave out every case that writes data. |
56
+ | `--allow-writes=<env>` | Run the write cases in that environment without the prompt. For unattended runs only; AI agents never use it. |
57
+ | `--step-text` | Overlay the current test step on the videos (`=1\|true\|yes\|on`; off by default). |
58
+ | `--update-baselines <case,case>` | Overwrite the screenshot baselines of exactly these cases. See below. |
59
+ | `--report-id <id>` | Name of the report folder (default: the timestamp). |
60
+ | `--ui`, other Playwright options | Passed through to Playwright. `--ui` opens its interactive mode. |
61
+
62
+ A case that writes data runs only in the environments it lists under `writes`; elsewhere it is skipped with a notice.
63
+ In **staging** no confirmation is needed. In any other environment a person confirms every run: `pnpm asrun` lists
64
+ the write cases and asks you to type the environment name; the web UI asks in a dialog. Unattended runs combine the
65
+ prompt with `--allow-writes=<env>`; AI agents must not. `--read-only` leaves the cases out instead.
66
+
67
+ ## Reports and evidence
68
+
69
+ Every run writes to `reports/<run-id>/`, where the run id is the timestamp and the environment
70
+ (`reports/2026-10-28T07-30-22-staging`), and points `reports/latest` at the newest one.
71
+
72
+ - **Video:** one per test, with the recordings of every browser tab joined in order into `reports/<run>/videos/`.
73
+ Joining needs ffmpeg; without it the report lists the recordings separately. `--step-text` writes the current step
74
+ on the video.
75
+ - **Trace:** the page before and after every action, plus every network request. Open a trace directly with
76
+ `pnpm exec playwright show-trace <trace.zip>`.
77
+ - **Screenshots:** on failure, and the baselines of visual checks with their diff.
78
+ - **Leftover test data:** listed in red in the report and in the terminal.
79
+
80
+ `pnpm report` opens the HTML report of the latest run. `pnpm asrun` also prints the paths of the videos and traces
81
+ when it finishes. Recordings contain real pages: do not share them outside the team.
82
+
83
+ ## Baselines
84
+
85
+ Baselines are per profile (`...-example-staging-darwin.png`) and are the source of truth for pixel checks.
86
+
87
+ Fix the spec first (the check must look at the element its target card names), then:
88
+
89
+ ```bash
90
+ pnpm asrun --env staging --profile example-staging --grep header-logo --update-baselines header-logo
91
+ pnpm baselines:review header-logo
92
+ ```
93
+
94
+ - `--update-baselines` lists the cases whose images are overwritten; they must be among the cases the filters run.
95
+ Playwright has one global switch, so those cases run as a second pass with their own report
96
+ (`<run-id>-baselines`, for example `2026-10-28T07-30-22-staging-baselines`); every other case is compared with its
97
+ baselines as usual.
98
+ - Without it, Playwright's own `--update-snapshots` still rewrites every image of the run.
99
+ - In the web UI every case in **Cases to run** has an **Update baselines** box (unticked by default), with a
100
+ confirmation, and the new images are shown next to their target cards when the run ends.
101
+ - An agent never updates a baseline: that is a person's decision.
102
+
103
+ ## Login and sessions
104
+
105
+ ```bash
106
+ pnpm status # every profile, or: pnpm status --env staging
107
+ pnpm signin example-staging
108
+ ```
109
+
110
+ The login is done by a person, including 2FA; the session is saved to `.auth/<profile-id>.json` and verified. See
111
+ [profiles.md](./profiles.md).
112
+
113
+ ## Cases and checks
114
+
115
+ ```bash
116
+ pnpm case:new header-logo
117
+ pnpm case:new documents/header-logo
118
+ pnpm case:check
119
+ pnpm spec:lint header-logo
120
+ pnpm baselines:review --new
121
+ pnpm pdf:render report.pdf --out .explore/targets/header-logo --corner top-left
122
+ ```
123
+
124
+ - `case:new` creates the case from the template. One optional group folder, created on demand.
125
+ - `case:check` validates front matter, values and target cards; see [cases.md](./cases.md).
126
+ - `spec:lint` is a static check of the specs: it fails on sleeps, `networkidle`, image checks without a target,
127
+ `.first()` on images, raw download events, a magic-number-only PDF check. `pnpm spec:lint <case-id>` limits it.
128
+ - `baselines:review` opens nothing; it lists every baseline next to the target card that says what it must show.
129
+ `--new` lists only the images that are new or changed (by git).
130
+ - `pdf:render` writes page images and prints the text, so a person (or an exploring agent) can look at a PDF.
131
+
132
+ ## Exploring and healing evidence
133
+
134
+ ```bash
135
+ pnpm explore example-staging /app/home
136
+ pnpm heal:evidence --case header-logo --env staging
137
+ ```
138
+
139
+ `explore` opens a logged-in browser at the profile's `startPath` (or the path you pass, or the site root). Downloads
140
+ are saved to `~/Downloads` and never overwrite an existing file. `heal:evidence` prints the last run's status, failed
141
+ step, error, call log and screenshots for a case, and whether the report is stale; it is what `/heal-spec` reads
142
+ first.
143
+
144
+ ## Cleanup
145
+
146
+ ```bash
147
+ pnpm cleanup --env staging
148
+ pnpm cleanup --env staging --forget
149
+ ```
150
+
151
+ A write case registers everything it creates (named `[asrun] ...`) and removes it after the test, also after a
152
+ failure. Whatever could not be removed is listed in the report and by `cleanup`. Delete it by hand in the product,
153
+ then run `--forget` so it is not listed again. `--env` is required.
154
+
155
+ ## Web UI
156
+
157
+ ```bash
158
+ pnpm go # install + build + start + open the browser (the usual command)
159
+ pnpm ui # build the web app and serve http://127.0.0.1:4173
160
+ pnpm ui:dev # Vite on :5173 with hot reload, API in watch mode
161
+ ```
162
+
163
+ The UI only listens on `127.0.0.1`. Set `ASRUN_UI_PORT` to change the port (`ASRUN_UI_WEB_PORT` for the Vite dev
164
+ server). See [web-ui.md](./web-ui.md).
165
+
166
+ ## Workspace
167
+
168
+ ```bash
169
+ pnpm asrun init [--update] [--force <file>] [--reset-rules] [--root <dir>] [--no-env] [--install [spec]]
170
+ pnpm asrun doctor
171
+ ```
172
+
173
+ `init` writes the workspace files; `--update` refreshes only the generated ones. `doctor` checks profiles,
174
+ `tsconfig.json`, `playwright.config.ts`, the generated files, the installed version, the project rule table and the
175
+ skill overrides. Full details in [install.md](./install.md).