@asrunjs/asrun 0.8.0 → 0.9.0

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 (36) hide show
  1. package/.claude/commands/explore-case.md +6 -2
  2. package/.claude/commands/gen-spec.md +4 -1
  3. package/.claude/commands/heal-spec.md +18 -7
  4. package/.claude/skills/exploring-cases/SKILL.md +19 -8
  5. package/.claude/skills/exploring-cases/references/open-questions.md +2 -2
  6. package/.claude/skills/generating-specs/SKILL.md +85 -30
  7. package/AGENTS.md +17 -9
  8. package/README.md +4 -3
  9. package/dist/ui/server/actions.js +9 -4
  10. package/dist/ui/shared/ai-commands.d.ts +6 -3
  11. package/dist/ui/shared/ai-commands.js +11 -7
  12. package/dist/ui/shared/types.d.ts +1 -1
  13. package/docs/ai-workflow.md +38 -11
  14. package/docs/theme-guide.html +93 -5
  15. package/docs/troubleshooting.md +7 -3
  16. package/docs/web-ui.md +28 -6
  17. package/package.json +1 -1
  18. package/templates/.claude/commands/explore-case.md +7 -2
  19. package/templates/.claude/commands/gen-spec.md +4 -1
  20. package/templates/.claude/commands/heal-spec.md +22 -12
  21. package/ui/dist/assets/{dist-ChBHX4rh.js → dist-B7HeglDd.js} +1 -1
  22. package/ui/dist/assets/{dist-CJiwxr1r.js → dist-B9_GXR2G.js} +1 -1
  23. package/ui/dist/assets/{dist-N-o4QIaZ.js → dist-Bb36rNUw.js} +1 -1
  24. package/ui/dist/assets/{dist-BpkTvixI.js → dist-DevASegc.js} +1 -1
  25. package/ui/dist/assets/{dist-DoChb6QQ.js → dist-DfBpjgZT.js} +1 -1
  26. package/ui/dist/assets/{dist-BgPbx2GZ.js → dist-Dq4Vnw5N.js} +1 -1
  27. package/ui/dist/assets/{dist-CavmZIv7.js → dist-Dqc_tfzC.js} +1 -1
  28. package/ui/dist/assets/{dist-Do_MhHKF.js → dist-G9KNeh6u.js} +1 -1
  29. package/ui/dist/assets/{dist-D6jmnDr-.js → dist-GvRUDL-2.js} +1 -1
  30. package/ui/dist/assets/{dist-BC_P1Gnf.js → dist-WvvCZeeu.js} +1 -1
  31. package/ui/dist/assets/index-BJ5D2mFK.js +346 -0
  32. package/ui/dist/assets/index-BXY-eKBn.css +2 -0
  33. package/ui/dist/help/case-explore.png +0 -0
  34. package/ui/dist/index.html +2 -2
  35. package/ui/dist/assets/index-Cy2l91qV.css +0 -2
  36. package/ui/dist/assets/index-DGQ5logJ.js +0 -338
@@ -7,12 +7,16 @@ In this repository the workspace is `examples/demo` (set `ASRUN_ROOT=examples/de
7
7
  Rules combine in this order, and a later one wins where they conflict: the framework baseline `AGENTS.md`, the
8
8
  framework skill, `.claude/skills/exploring-cases.md` if it exists, then the workspace `AGENTS.md` §2 in `examples/demo`
9
9
  (Project rules). F1–F6 always win.
10
- Arguments: `<case-id> <env> [--attended]`. The case id is the path below cases/ without `.md` (it may include a group
10
+ Arguments: `<case-id> <env> [--attended] [--profiles a,b]`. The case id is the path below cases/ without `.md` (it may include a group
11
11
  folder, e.g. documents/landing-page) and is required. `<env>` is `staging` or `production`; it is required and there is no
12
12
  default (without one, stop and say so). With `--attended` I am at the machine and the browser window is visible: when a step
13
13
  writes data or needs a choice, hand it to me as the skill describes, wait for me, and record what I did as a general
14
14
  recipe under the step it belongs to (`- Recipe:` lines). Without `--attended`, never wait for me.
15
+ `--profiles a,b` explores only those profiles (comma-separated ids from profiles.yaml); without it, at least one
16
+ logged-in profile per domain. A profile the list leaves out is not opened and nothing is written about it: runs add up,
17
+ so a later run covers the rest.
15
18
  Follow `assertion-targets.md` (list the candidates, never pick when in doubt, ask for the exact image/text/place/UI)
16
19
  and `readiness.md` (wait for the key requests and the content before you read anything) of the skill.
17
20
  This command edits cases/<case-id>.md only (plus screenshots under `.explore/targets/`). Do not ask me questions in the chat: write open questions into the case
18
- file as the skill describes, and end with the `RESULT:` line.
21
+ file as the skill describes, and end with the `RESULT:` line. If you are not allowed to write files at all (a plan-mode
22
+ run, a read-only sandbox), say what blocked you and end with `RESULT: needs-input`.
@@ -10,7 +10,10 @@ framework skill, `.claude/skills/generating-specs.md` if it exists, then the wor
10
10
  Arguments: `<case-id> [<env>] [--force]`. The case file is cases/<case-id>.md (the id may include one group folder, e.g.
11
11
  documents/landing-page). `<env>` is the environment to explore in (`staging` or `production`); there is no default. If the spec already exists, it is
12
12
  left alone unless `--force` is given; with `--force` it is regenerated and overwritten.
13
+ There is no profile option: the spec is generated for every profile the case holds information for (its `## Values`
14
+ keys, its target cards and its observations), not for a chosen subset and not only for the profiles that are logged in.
13
15
  Follow the skill's procedure exactly, including `assertion-targets.md` and `readiness.md`; run `pnpm spec:lint <case-id>`. Only generate the spec: do not run `pnpm asrun` (running specs is a separate
14
16
  process), and do not create baselines.
15
17
  Do not ask me questions: resolve ambiguity as the skill describes (explore the page, use observed values marked for QA,
16
- write the remaining questions into the case file), and end with the `RESULT:` line.
18
+ write the remaining questions into the case file), and end with the `RESULT:` line. If you are not allowed to write
19
+ files at all (a plan-mode run, a read-only sandbox), say what blocked you and end with `RESULT: needs-input`.
@@ -1,8 +1,8 @@
1
1
  ---
2
- description: Diagnose and fix a failing spec from its last report (without hiding real product regressions)
2
+ description: Repair a failing spec, or refine a case and its spec on request, without hiding real product regressions
3
3
  ---
4
4
 
5
- Load the `generating-specs` skill, section "Healing a failing spec", and follow it exactly.
5
+ Load the `generating-specs` skill, section "Healing and refining a spec", and follow it exactly.
6
6
  In this repository the workspace is `examples/demo` (set `ASRUN_ROOT=examples/demo`); the framework is `.`.
7
7
  Rules combine in this order, and a later one wins where they conflict: the framework baseline `AGENTS.md`, the
8
8
  framework skill, `.claude/skills/generating-specs.md` if it exists, then the workspace `AGENTS.md` §2 in `examples/demo`
@@ -11,14 +11,25 @@ Arguments: $ARGUMENTS
11
11
  Format: `<case-id> <env> [--profiles a,b] [--report <run-id>] [--hints <file>]`, with optional free text after them.
12
12
  - `<case-id>` and `<env>` (`staging` or `production`) are required. There is no default environment. Without one, stop
13
13
  and say so.
14
- - `--profiles`: the profiles to heal. Without it, every profile that is logged in in `<env>`.
14
+ - `--profiles`: the profiles to run when evidence is needed. Without it, every profile that is logged in in `<env>`.
15
15
  - `--report`: the report (folder name under `reports/`) to read first. Without it, the newest report of this case.
16
- - `--hints <file>`: a file with what the user knows (what to heal, evidence, clues). Read it first. Any free text
17
- after the arguments is the same kind of clue. Both are optional.
18
- Before you decide "product or test", audit the assertion itself (target card, baseline content, readiness): see steps 4
16
+ - `--hints <file>`: a file with what the user wants repaired or improved, with any evidence. Read it first. Any free
17
+ text after the arguments is the same kind of request. Both are optional.
18
+ A passing run is not a reason to refuse a request: healing also refines a case and its spec. Never lower the bar for a
19
+ product finding to make a test pass.
20
+
21
+ Do not ask me questions in the chat: this may run unattended. Resolve what the report, the case and the page can
22
+ resolve, and write what still needs a person into the case as a `[blocking]` question. End with the `RESULT:` line:
23
+ `ready` when the healing or the refinement is finished, `needs-input` when something is still open. If you could not
24
+ write files at all (a plan-mode run, a read-only sandbox, a refused permission), say in one sentence what blocked you
25
+ and end with `RESULT: needs-input`: never answer with a plan and a question instead.
26
+
27
+ Before you decide "product or test", audit the assertion itself (target card, baseline content, readiness): see step 5
19
28
  of the skill's healing section, `assertion-targets.md` and `readiness.md`. Run `pnpm spec:lint <case-id>`.
20
29
  Read the existing report first (`pnpm heal:evidence --case <case-id> --env <env>`), and run the case again only when the
21
- skill says it is needed. Always run with `--logged-in`. Tell me whether it is a test problem or a product finding.
30
+ skill says it is needed. Always run with `--logged-in`. If the change leaves an open question or an unconfirmed marker
31
+ in the case, stop there, tell me to review the case and run `/explore-case`, and end with `RESULT: needs-input`.
32
+ Otherwise tell me whether it is a test problem or a product finding.
22
33
 
23
34
  Run `pnpm asrun ...`, `pnpm heal:evidence ...`, `pnpm spec:lint ...`, `pnpm baselines:review ...` and `pnpm status ...` as plain commands, optionally piped to `tail` or
24
35
  `head`, and do not chain other commands with them (`;` or `&&`). Only these exact forms run outside Claude Code's
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: exploring-cases
3
- description: Explores the real pages for one case document (cases/<id>.md) before its spec is generated. Checks the case against profiles.yaml and the pages, fixes wording mistakes, records what the pages show, turns unknowns into answerable TODO blocks, and, in an attended run, lets the person do the steps that write data or need a choice while it waits and records a general recipe under that step's `- Recipe:` lines. Ends with RESULT ready or needs-input. Use when asked to explore, review, check, clean up or narrow down a case, or for /explore-case. Its open-question rules are shared with generating-specs.
3
+ description: Explores the real pages for one case document (cases/<id>.md) before its spec is generated, for every logged-in profile or only the ones named with --profiles. Checks the case against profiles.yaml and the pages, fixes wording mistakes, records what the pages show, turns unknowns into answerable TODO blocks, and, in an attended run, lets the person do the steps that write data or need a choice while it waits and records a general recipe under that step's `- Recipe:` lines. Ends with RESULT ready or needs-input. Use when asked to explore, review, check, clean up or narrow down a case, or for /explore-case. Its open-question rules are shared with generating-specs.
4
4
  ---
5
5
 
6
6
  # Exploring a case document
@@ -34,8 +34,10 @@ under `.auth/`.
34
34
  choice (see `references/user-handoff.md`).
35
35
 
36
36
  It never stops to ask in the chat. A question that cannot be answered from the page goes into the case file, where
37
- the user answers it by editing the file, and the report says `RESULT: needs-input`. The one exception is a handoff in
38
- an attended run, where waiting for the person is the point.
37
+ the user answers it by editing the file, and the report says `RESULT: needs-input`. This holds whatever mode or agent
38
+ the run is in: if you are not allowed to write files (plan mode, a read-only sandbox, a refused permission), say what
39
+ blocked you in one sentence and end with `RESULT: needs-input` instead of answering with a plan and a question. The
40
+ one exception is a handoff in an attended run, where waiting for the person is the point.
39
41
 
40
42
  ## Inputs
41
43
  - A case id (path below `cases/` without `.md`, e.g. `documents/landing-page`). It is required: an attended exploration of
@@ -44,13 +46,18 @@ an attended run, where waiting for the person is the point.
44
46
  before exploring and report `RESULT: needs-input` ("pass the environment").
45
47
  - Optionally `--attended`: a person is at the machine and the browser window is visible, so handoffs may happen.
46
48
  Without it nothing waits, and a step that needs a person becomes a `[blocking]` TODO.
49
+ - Optionally `--profiles a,b`: explore only these profiles (ids from `profiles.yaml`, comma-separated). Without it, at
50
+ least one logged-in profile per domain. The selection is never widened: a profile it leaves out is not opened and
51
+ nothing about it is written into the case. Runs add up: a later run with other profiles adds their observations and
52
+ `## Values` entries next to the ones recorded before.
47
53
 
48
54
  ## Procedure
49
55
  1. **Read** the case, `profiles.yaml` and `cases/_template.md`. Apply uncommitted user edits as they are: they are the
50
56
  newest wording. Use targeted edits, never rewrite a whole file.
51
57
  2. **Static review.** Find:
52
58
  - missing values for a profile: compare the entries of `## Values` (keyed by `default` and profile ids) with the
53
- ids in `profiles.yaml`. A missing entry is a gap, not something to invent: leave it to the observations and
59
+ ids in `profiles.yaml` (with `--profiles`, only those ids: the rest belong to a run of their own). A missing
60
+ entry is a gap, not something to invent: leave it to the observations and
54
61
  `/gen-spec`. The case's `## Values` is the only place for expected values; never copy them into `profiles.yaml`;
55
62
  - ambiguous steps and checks without an expected result;
56
63
  - steps that write data or need a choice: mark them `[writes]` / `[input]`. The front matter must list the
@@ -70,9 +77,11 @@ an attended run, where waiting for the person is the point.
70
77
  `RESULT: needs-input`.
71
78
  Run `pnpm status --env <env>`. If it cannot run (a sandbox error such as
72
79
  `listen EPERM` or a Chromium start failure), do not give up: open the profile's page through the MCP, and a page
73
- that lands on the login page means the profile is not logged in. Explore at least one logged-in profile per
74
- domain with the `playwright-test` MCP (`generator_setup_page` with `project=<profile-id>` and
75
- `seedFile=tests/seed.spec.ts`). Profiles that are not logged in are skipped and named in the report; a human runs
80
+ that lands on the login page means the profile is not logged in. Explore each profile `--profiles` names that is
81
+ logged in, and, without `--profiles`, at least one logged-in profile per domain, with the `playwright-test` MCP
82
+ (`generator_setup_page` with `project=<profile-id>` and
83
+ `seedFile=tests/seed.spec.ts`). Never explore a profile the selection leaves out, and never add a profile to fill
84
+ a gap. Profiles that are selected but not logged in are skipped and named in the report; a human runs
76
85
  `pnpm signin <profile-id>`.
77
86
  - Do the read-only steps yourself: open, look, read, hover, switch tabs, open and close dialogs and menus.
78
87
  - **A step that writes or needs a choice is done by the person**, per `references/user-handoff.md`: announce it,
@@ -129,7 +138,8 @@ an attended run, where waiting for the person is the point.
129
138
  8. **Check**: run `pnpm case:check` (it fails on an incomplete target card and warns on a check that has none) and fix every FAIL. If it fails with a sandbox error (`listen EPERM`), run
130
139
  `node --import tsx <framework>/scripts/check-cases.ts` instead; it checks the same thing.
131
140
  9. **Report** in the user's language: what was changed (wording fixes, observations added, TODOs closed or narrowed),
132
- what was explored (profile ids), which steps the person did and what was recorded for them, what was skipped and
141
+ what was explored (profile ids), which profiles `--profiles` left out (and that a run with them covers the rest),
142
+ which steps the person did and what was recorded for them, what was skipped and
133
143
  why, the target cards written (and which ones are still questions), the steps whose first read was too early,
134
144
  the steps that could not be observed (and why), the questions that remain with the sentence that deleting each block would confirm, and findings that look
135
145
  like product bugs. End with the `RESULT:` line.
@@ -141,6 +151,7 @@ an attended run, where waiting for the person is the point.
141
151
  - Never invent expected values, profile names, counts or UI texts. Observations are marked as such. Values that a
142
152
  check asserts are written into the case's `## Values` under the profile id (or `default` when they hold for every
143
153
  profile), and never into `profiles.yaml`.
154
+ - Never explore a profile that `--profiles` leaves out, and never widen the selection to cover a gap in the case.
144
155
  - Never pick a target when in doubt, and never record a fact that was read before the content finished loading.
145
156
  - Never read or print anything under `.auth/`. Never automate login or 2FA.
146
157
  - Prose follows the artifact language of the workspace `AGENTS.md` §2 (English by default); structural tokens stay English.
@@ -110,7 +110,7 @@ RESULT: needs-input
110
110
 
111
111
  - `ready`: no `[blocking]` question remains. Non-blocking ones may remain; list them so QA can confirm them.
112
112
  - `needs-input`: at least one `[blocking]` question remains in the case file (list each with its parent sentence),
113
- or the work could not be done (no environment, nobody logged in). Say what the user should edit in the case file,
114
- then re-run.
113
+ or the work could not be done (no environment, nobody logged in, or no permission to write files: a plan-mode run,
114
+ a read-only sandbox). Say what the user should edit in the case file, then re-run.
115
115
 
116
116
  Do not write anything after the `RESULT:` line.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: generating-specs
3
- description: Turns a human-written test case (cases/<id>.md, the id may include a group folder such as documents/landing-page) into a Playwright spec (tests/<id>.spec.ts) for the multi-profile test suite, explores the real pages to learn the locators and values, and completes the case's own "## Values" block. It only generates: it never runs the tests. Use when asked to generate, update, fix or heal a spec, or for /gen-spec.
3
+ description: Turns a human-written test case (cases/<id>.md, the id may include a group folder such as documents/landing-page) into a Playwright spec (tests/<id>.spec.ts) for the multi-profile test suite, explores the real pages to learn the locators and values, and completes the case's own "## Values" block. It only generates: it never runs the tests. Healing (repairing a failing spec, or refining a case and its spec on request) is the exception. Use when asked to generate, update, fix, heal or refine a spec, or for /gen-spec or /heal-spec.
4
4
  ---
5
5
 
6
6
  # Generating specs from case documents
@@ -12,16 +12,26 @@ defines how open questions are written into the case file, resolved and reported
12
12
  `../exploring-cases/references/readiness.md` (wait for the content, verify downloads in five parts). All three are binding here.
13
13
 
14
14
  This skill never stops to ask the user in the chat, because it may run without anybody present (a one-shot
15
- `claude -p` / `opencode run`). Questions go into the case file; the final line is `RESULT: ready` or
16
- `RESULT: needs-input`.
15
+ `claude -p` / `opencode run`). This holds whatever mode or agent the run is in: plan mode changes what you may write,
16
+ never how you end. If you are not allowed to write files (plan mode, a read-only sandbox, a refused permission), say
17
+ what blocked you in one sentence and end with `RESULT: needs-input` instead of answering with a plan and a question.
18
+ Questions go into the case file; the final line is `RESULT: ready` or `RESULT: needs-input`.
17
19
 
18
20
  This skill trusts the case. What the pages show, what a person has to enter, and what the open questions are is
19
21
  settled by `/explore-case` and recorded in the case (`(observed, ...)` marks, the step recipes). Generating
20
22
  only finds locators and writes code: it replays the case and does not judge the case again.
21
23
 
24
+ The profile scope is the case's, not a selection. It is every profile id that appears in the case — the keys of
25
+ `## Values`, a target card's `Reference <profile-id>:` line, a step observation — plus the `default` entry. There is no
26
+ `--profiles` here: never narrow the scope to the profiles that are logged in, and never ask for a subset. The spec runs
27
+ once per profile in the environment, so it must cover every profile the case says something about. A profile the case
28
+ says nothing about runs on `default` alone; name those in the report. Runs of `/explore-case` add up, so a case explored
29
+ with A and B first and C later generates a spec for all three.
30
+
22
31
  Generating a spec and running it are separate processes. This skill only writes files: it does not run
23
32
  `pnpm asrun`, does not create screenshot baselines and does not judge whether the spec passes. The user runs the
24
- spec afterwards (see the report). Healing a failing spec (below) is the exception: it reads the last report first and runs only the profiles that are logged in, and only when the report is not enough.
33
+ spec afterwards (see the report). Healing (below) is the exception: it reads the last report first, may edit the case
34
+ and the spec, and runs only the profiles that are logged in, and only when the report is not enough.
25
35
 
26
36
  ## Before you start
27
37
  Read the workspace `AGENTS.md`: its §2 holds the project rules, and it points at the framework baseline. If
@@ -33,6 +43,8 @@ continue with the procedure below.
33
43
  - The case id and the environment to explore in (`staging` or `production`). There is no default. If the
34
44
  environment is missing, stop before exploring and report `RESULT: needs-input` ("pass the environment").
35
45
  - Optionally `--force`: regenerate the spec from scratch and overwrite the existing one. See "An existing spec".
46
+ - No profile selection: the profile scope is the case's (see above). A case id and an environment are all this command
47
+ takes besides `--force`.
36
48
 
37
49
  A case id is its path below `cases/` without `.md`: `hub-landing`, or `documents/landing-page` when the case is in a
38
50
  folder (one level only). The spec is at the same path below `tests/` (`tests/documents/landing-page.spec.ts`); both import
@@ -94,9 +106,11 @@ Decide this first, before exploring anything. The spec path is `tests/<id>.spec.
94
106
  - Do not invent expected values. If the case's `## Values` and the spec disagree, the case wins: fix the spec and
95
107
  mention the change in the report.
96
108
  3. **Check sessions** if `login: required`: run `pnpm status --env <env>`. If a profile needs login, tell the
97
- user to run `pnpm signin <profile-id>` and continue with the profiles that are ready. If `pnpm status`
98
- cannot run (a sandbox error such as `listen EPERM` or a Chromium start failure), judge by the page instead: a page
99
- that lands on the login page means the profile is not logged in.
109
+ user to run `pnpm signin <profile-id>` and continue with the profiles that are ready. A profile the case holds
110
+ information for is never dropped because it is not logged in: write its part from the case and mark it
111
+ `not verified on the page` in the report. If `pnpm status` cannot run (a sandbox error such as `listen EPERM` or a
112
+ Chromium start failure), judge by the page instead: a page that lands on the login page means the profile is not
113
+ logged in.
100
114
  4. **Replay the case on the real page** with the `playwright-test` MCP. If its tools are not registered in this
101
115
  session (no `browser_*` / `generator_*` tool) or it reports that it failed to start, do not fall back to ad-hoc
102
116
  Playwright scripts: stop, tell the user which MCP server is missing and to run `pnpm asrun doctor`, write no spec,
@@ -109,10 +123,11 @@ Decide this first, before exploring anything. The spec path is `tests/<id>.spec.
109
123
  of the spec from the `- Recipe:` lines under the step (positions, `[asrun]` names, control labels and roles that
110
124
  `/explore-case` recorded). Pages that only exist after such a step cannot be seen from here: write their locators
111
125
  from what the recipe and the exploration notes say, mark them in the report as "not verified on the page", and
112
- leave the first run of the spec (or `/heal-spec`) to confirm them. Do this for at least one profile
113
- per domain (example.com and exampleshop.com differ in markup). Profiles that are not logged in are skipped and
114
- named in the report. Use this exploration to answer the open points of step 2, and look at every profile that has
115
- an entry in `## Values` to read its value.
126
+ leave the first run of the spec (or `/heal-spec`) to confirm them. Do this for at least one logged-in profile
127
+ per domain (example.com and exampleshop.com differ in markup). A profile the case holds information for that is
128
+ not logged in is skipped and named in the report; its part is written from the case and marked `not verified` in
129
+ the assertion table. Use this exploration to answer the open points of step 2, and read each profile's value from
130
+ the case's `## Values`: open that profile's page only when a value or a behavior still has to be resolved for it.
116
131
  Keep every browser call short (one action, about 60 s at most, a snapshot between steps, `{timeout: 5000}` when
117
132
  something may not appear, no third retry): see "Keep every browser call short" in `../exploring-cases/SKILL.md`.
118
133
  **Ready before you read, here too** (`readiness.md`): `browser_network_clear`, act, wait for the key requests
@@ -126,8 +141,11 @@ Decide this first, before exploring anything. The spec path is `tests/<id>.spec.
126
141
  `(observed, 🔍 to be confirmed by QA)`, remove the TODO blocks they answer (rewrite the sentence above the block),
127
142
  and narrow the ones that remain. Keep the shape of `open-questions.md` for every block. Edit the case
128
143
  with targeted edits; never touch its `## Source` table.
129
- 5. **Complete the case's `## Values`**: for every profile the spec runs for, add the entry with the literals the spec
130
- asserts (the same keys as `default`, only what differs). Do not move values into `profiles.yaml`; it holds connection
144
+ 5. **Complete the case's `## Values`**: for every profile the case holds information for, add the entry with the
145
+ literals the spec asserts (the same keys as `default`, only what differs); put a literal that holds for every profile
146
+ in `default`. Do not add an entry for a profile the case says nothing about, and never invent a value to fill one: a
147
+ profile without an entry runs on `default` alone, and a literal the case does not hold is a question in the case.
148
+ Do not move values into `profiles.yaml`; it holds connection
131
149
  settings only. Mention every value you added or corrected in the report.
132
150
  6. **Write `tests/<id>.spec.ts`** (inside the group folder if the id has one, creating it if needed) following the conventions. One `test()` per outcome, and every
133
151
  case step wrapped in `test.step()` with the case's step text as the title, and the top-level
@@ -150,7 +168,8 @@ Decide this first, before exploring anything. The spec path is `tests/<id>.spec.
150
168
  Do NOT run `pnpm asrun`, do not use
151
169
  `--update-snapshots` and do not create baselines: the first run of the spec does that, and a person reviews them.
152
170
  8. **Report** in the user's language: spec path, **an assertion table** (step, what is asserted, target card, ready signal, and
153
- `observed on the page` or `not verified`; no row may be missing), profiles explored, profiles skipped (and why), whether the case file was
171
+ `observed on the page` or `not verified`; no row may be missing), profiles explored, profiles skipped (and why),
172
+ the profiles of `profiles.yaml` the case holds no information for (they run on `default` alone), whether the case file was
154
173
  translated, what was added to the case file as observed, checks generated from observations (QA must confirm
155
174
  them), visual checks that need a baseline on the first run, and the remaining TODO blocks (`[blocking]` ones with
156
175
  the sentence that deleting the block would confirm). State that the spec was not run and give the command to run it:
@@ -166,34 +185,51 @@ spec does, when a person runs it in an environment that `writes` lists. Elsewher
166
185
  notice. Never send email in production; in other environments only if `profile.email` is true, otherwise skip that step and say so
167
186
  in the report.
168
187
 
169
- ## Healing a failing spec
188
+ ## Healing and refining a spec
170
189
  Inputs: the case id and the environment (both required, there is no default: without an environment stop and say so),
171
190
  optionally `--profiles a,b`, `--report <run-id>` and `--hints <file>` (or free text after the arguments). Healing is
172
191
  the one case where this skill runs specs, and only in the environment given.
173
192
 
193
+ Healing has two jobs, and the same command does both: **repair** a spec that fails, and **refine** a case and its spec
194
+ when the user asks for a change. A passing run is never a reason to refuse a request, and "nothing failed" is never a
195
+ reason to do nothing when the user asked for something.
196
+
174
197
  Heal from evidence, not from a habit of rerunning. A report that already exists says what failed.
175
198
 
176
- 1. **Read the user's clues first** (the `--hints` file, or the free text). They say what to look at and may hold
177
- evidence the report lacks. They guide the diagnosis and nothing else: they never lower the bar for a product
178
- finding and never allow an expectation to be changed just to make a test pass. Quote the clue you used, or say that
179
- it did not hold up, in the report.
180
- 2. **Read the existing report**: run `pnpm heal:evidence --case <case-id> --env <env>` (add `--profile a,b` when `--profiles`
199
+ 1. **Read the user's request first** (the `--hints` file, or the free text). It says what to repair or improve and may
200
+ hold evidence the report lacks. Decide which kind of work it is:
201
+ - a failure to repair (the last run failed, or the user names one);
202
+ - an improvement, even when the last run passed: a check that is too loose, a step, expectation, value or target
203
+ that no longer matches the page, wording a person cannot follow;
204
+ - both.
205
+ Quote the request you used, or say that it did not hold up, in the report. It guides the work and nothing else: it
206
+ never lowers the bar for a product finding and never allows an expectation to be changed just to make a test pass.
207
+ - **No report and no request:** do not invent work. Say that nothing asks for a change, list what
208
+ `pnpm spec:lint <case-id>` and the last report call weak (as suggestions, not edits), and end with `RESULT: ready`.
209
+ - **A passing report and no request:** the same. Do not start an improvement of your own.
210
+ 2. **Read the existing report** if there is one: run `pnpm heal:evidence --case <case-id> --env <env>` (add `--profile a,b` when `--profiles`
181
211
  was given). It reads `reports/*/summary.json` and prints, per profile, the status, the failed step, the error, the call
182
212
  log, the screenshots and videos, and ends with `EVIDENCE: fresh | stale | no-failure | none`. With `--report <run-id>`,
183
213
  read `reports/<run-id>/summary.json` instead. Look at the screenshot when the error alone does not explain it. A profile
184
- that failed only because it was not logged in is no evidence about the spec.
214
+ that failed only because it was not logged in is no evidence about the spec. `EVIDENCE: none` or `no-failure` with an
215
+ improvement request is a starting point, not a stop.
185
216
  3. **Decide whether to rerun the case**. Do not rerun when the report is `fresh`, names the failing step and the error,
186
217
  and that is enough to tell whether the product changed or the test is wrong. Rerun when:
187
218
  - there is no report (`none`), or it is `stale` (the spec, the case or `profiles.yaml` changed after it);
188
219
  - the report does not explain the failure (a bare timeout with no locator, a failure before any step, a truncated log);
189
- - a clue from the user has to be checked against the real page;
220
+ - a clue or a requested improvement has to be checked against the real page, and no recent report shows it;
190
221
  - a profile that must be healed has no result in the report (for example it was not logged in then).
191
222
  Rerun with one plain command, as few profiles as possible, and say why in the report:
192
223
  `pnpm asrun --env <env> --logged-in --profile <ids> --grep <case-id>` (optionally piped to `tail`; do not chain it
193
224
  with other commands, or it runs inside Claude's sandbox and fails with `listen EPERM`). Always add `--logged-in`: it
194
225
  leaves out the profiles that are not logged in instead of failing them. A profile that is not logged in is never healed:
195
226
  name it in the report with `pnpm signin <profile-id>`.
196
- 4. **Audit the assertion before you blame anyone.** Read the `assertion-targets.md` and `readiness.md` sections on healing.
227
+ 4. **Look at the pages when the change depends on them.** Reading a page is not a run and is always allowed, also for an
228
+ improvement request with no failure behind it. Use the `playwright-test` MCP exactly as `/gen-spec` step 4 describes
229
+ (`generator_setup_page` with `project=<profile-id>`, one action per call, ready before you read). Do the read-only
230
+ steps only: **never perform a step that writes or needs a choice**, in any environment. What you see goes into the
231
+ case as `(observed, 🔍 to be confirmed by QA)`, so the person can confirm it in the review gate below.
232
+ 5. **Audit the assertion before you blame anyone.** Read the `assertion-targets.md` and `readiness.md` sections on healing.
197
233
  - Is the locator still what the target card says (`What`, `Identified by`)? Open the failure screenshot and the
198
234
  baseline (`pnpm baselines:review <case-id>`) and look at each: a baseline that shows something other than the card
199
235
  (a content image instead of a logo) is a wrong test, and passing runs on it prove nothing. Delete it and report it; a
@@ -204,16 +240,35 @@ Heal from evidence, not from a habit of rerunning. A report that already exists
204
240
  - A download that fails says which stage failed (start, finish, exists, valid): fix the stage, do not loosen the check.
205
241
  - Doubt about which element is meant: write the question into the case (a `[blocking]` TODO naming the exact image,
206
242
  text, place or UI) instead of choosing; do not rewrite the card quietly.
207
- 5. **Decide whether the PRODUCT changed or the TEST is wrong.**
243
+ 6. **Decide what the change belongs to, and change the case first.**
208
244
  - Locator/timing problem: fix the spec.
209
245
  - A real product difference (wrong logo, wrong name): do NOT change the expectation to make it
210
246
  pass. Report it as a finding with the trace/screenshot path.
211
- 6. **After editing the spec, run it once to confirm** (same command as in step 3, for the profiles you healed). That rerun
212
- is the only one that is always allowed. Run `pnpm typecheck` too.
213
- 7. **Report** in the user's language: which report was read (or that there was none) and whether it was fresh or stale,
214
- whether the case was run again and why (or why not), what was changed, what is a product finding (with paths),
215
- which assertions were wrong or raced the page, which baselines are wrong and were deleted, what the user's clues contributed, which profiles were left out because they are not logged in, and the result of the
216
- confirming run.
247
+ - A request the case can settle: the case is the source of truth, so write the change into `cases/<case-id>.md`
248
+ first, then into the spec. That covers a step, an expectation, a value in `## Values`, a target card, a ready
249
+ signal and a recipe. The spec follows the case (its step titles copy the case's step text). When the case changed
250
+ more than a sentence (a new or removed step, different expected values, another target), regenerate the spec from
251
+ the case as "## Procedure" steps 5–6 describes instead of patching it; otherwise edit it in place. Say in the
252
+ report which of the two you did.
253
+ 7. **Stop at the review gate.** After editing the case, read it back. If **this run** left a `🚫 TODO [blocking]` or
254
+ `❓ TODO [non-blocking]` block, or a `🔍 to be confirmed by QA` or `👁️ not observed` marker, do not edit or
255
+ regenerate the spec any further: a case that still asks something is not a source for a spec. Report exactly what
256
+ to review, that answering or deleting those blocks comes first, and that the next command is
257
+ `/explore-case <case-id> <env>` (with `--attended` when a step writes data). End with `RESULT: needs-input`. Blocks
258
+ and markers that were already in the case before this run and that it did not touch do not stop it.
259
+ 8. **After editing the spec, run it once to confirm** (same command as in step 3, for the profiles you healed). That rerun
260
+ is the only one that is always allowed. Run `pnpm typecheck`, `pnpm spec:lint <case-id>` and `pnpm case:check`, and
261
+ fix every failure of the checks you touched. An improvement may make the run fail on purpose: a tightened check that
262
+ now fails is evidence about the product, so report it as a finding instead of loosening it again.
263
+ 9. **Report** in the user's language: which report was read (or that there was none) and whether it was fresh or stale,
264
+ whether the case was run again and why (or why not), what the request asked for and what came of it, what was
265
+ changed in the case and in the spec (and whether the spec was regenerated), what is a product finding (with paths),
266
+ which assertions were wrong or raced the page, which baselines are wrong and were deleted, what the user's request
267
+ contributed, which profiles were left out because they are not logged in, and the result of the
268
+ confirming run. The last line is `RESULT: ready` (nothing left for a person, the spec changed and its checks pass)
269
+ or `RESULT: needs-input` (the case waits for a review, as in step 7, or a person has to decide something). A run
270
+ that could not write files at all (plan mode, a read-only sandbox, a refused permission) is `needs-input` too:
271
+ never end with a plan and a question instead.
217
272
 
218
273
  A case that writes data in `production` needs a person's confirmation at run time, so the rerun is refused there:
219
274
  use the report, or heal in staging, and never add `--allow-writes`.
package/AGENTS.md CHANGED
@@ -143,14 +143,16 @@ The framework folder is read-only in a project: never edit it. Its own layout an
143
143
  Three steps, each learns one thing: the intent, the facts and inputs, the implementation.
144
144
  1. `making-cases` (or `/make-case <notes>`) turns raw notes into case documents. It writes `cases/*.md` only and
145
145
  never opens a browser. Check them with `pnpm case:check`.
146
- 2. `exploring-cases` (or `/explore-case <case-id> <env> [--attended]`) explores the real pages for one case. It
147
- edits that case file only: wording, observations, TODO blocks, and, with `--attended`, the `- Recipe:` lines of
148
- the steps that write or need a choice.
146
+ 2. `exploring-cases` (or `/explore-case <case-id> <env> [--attended] [--profiles a,b]`) explores the real pages for one
147
+ case. It edits that case file only: wording, observations, TODO blocks, and, with `--attended`, the `- Recipe:` lines
148
+ of the steps that write or need a choice. `--profiles` narrows the run to those profiles; runs add up, so the case
149
+ can be explored in passes.
149
150
  3. `generating-specs` (or `/gen-spec <case-id> <env>`) turns the case into a spec. It replays the case and its
150
151
  step recipes and `## Values`, writes the spec (values stay in the case document), but never runs the tests, never
151
- writes data and never creates baselines. A tagged step with no `- Recipe:` line (an old `## Inputs` line counts
152
- until it is moved) stops with `needs-input`: run `/explore-case` first. Running specs is a separate step
153
- (`pnpm asrun`, or the Run page).
152
+ writes data and never creates baselines. There is no profile selection here: it covers every profile the case names,
153
+ which is why a case explored in passes generates one spec for all of them. A tagged step with no `- Recipe:` line
154
+ (an old `## Inputs` line counts until it is moved) stops with `needs-input`: run `/explore-case` first. Running specs
155
+ is a separate step (`pnpm asrun`, or the Run page).
154
156
  Workspace maintenance is separate from the AI steps: `pnpm asrun upgrade` installs the newest framework, refreshes the
155
157
  generated files and applies the release's workspace migrations (it checks the target release for breaking changes
156
158
  before installing), `pnpm asrun init [--update] [--force]` writes or refreshes the workspace files, `pnpm asrun
@@ -161,9 +163,15 @@ Do not hand-write specs unless asked. Do not edit `lib/` to make a single spec p
161
163
  Every case step is wrapped in `test.step('<n>. <case step text>', ...)` so reports and traces show the
162
164
  case's own steps (see the skill's `references/conventions.md`).
163
165
  Every AI command needs an environment (`--env`/`<env>`); there is no default and no "no environment" mode.
164
- `/heal-spec <case-id> <env>` reads the existing report first (`pnpm heal:evidence --case <id> --env <env>`), reads the
165
- user's optional clues (`--hints <file>`), runs only the profiles that are logged in (`--logged-in`) and runs the case
166
- again only when the report is missing, stale or not enough.
166
+ A one-shot AI run is a build run: it writes the case or the spec, never a plan. AsRun starts OpenCode with
167
+ `--agent build`, because OpenCode keeps the agent (build or plan) on the session and a plan session would only propose
168
+ changes and ask a question nobody can answer. Whatever mode or sandbox a run finds itself in, it still ends with the
169
+ `RESULT:` line: a run that was not allowed to write files is `needs-input`, never a plan and a question.
170
+ `/heal-spec <case-id> <env>` repairs one failing spec or refines a case and its spec on request (a passing run is not
171
+ a reason to refuse): it reads the existing report first (`pnpm heal:evidence --case <id> --env <env>`), the user's
172
+ request (`--hints <file>`), runs only the profiles that are logged in (`--logged-in`) and runs the case again only
173
+ when the report is missing, stale or not enough. A change that leaves an open question or an unconfirmed observation
174
+ in the case stops before the spec: the person reviews the case and runs `/explore-case` again.
167
175
  Run `pnpm typecheck`, `pnpm spec:lint <case-id>` and `pnpm case:check` before reporting that a spec was generated. Run
168
176
  `pnpm asrun --env <env> --profile <id> --grep <case-id>` only when healing a spec or when asked to.
169
177
  Pick `--env staging` while developing. A case that does not list `production` under `writes` is skipped in
package/README.md CHANGED
@@ -13,8 +13,8 @@ AsRun is a devDependency, not a service. Your profiles, cases, specs and baselin
13
13
  - **One spec, every profile.** Each entry in `profiles.yaml` becomes a Playwright project with its own base URL,
14
14
  saved login and screenshot baselines. Two accounts of one site give two independent runs.
15
15
  - **An AI workflow, not a prompt dump.** `/make-case` turns raw notes into cases, `/explore-case` checks them against
16
- the real pages, `/gen-spec` writes the spec, `/heal-spec` repairs it from the last report. Each command ends with
17
- `RESULT: ready` or `RESULT: needs-input`, so it works unattended.
16
+ the real pages, `/gen-spec` writes the spec, `/heal-spec` repairs it from the last report or refines a case and its
17
+ spec on request. Each command ends with `RESULT: ready` or `RESULT: needs-input`, so it works unattended.
18
18
  - **Evidence for every run.** One video per test (browser tabs joined), a trace with every action and request,
19
19
  screenshots on failure, and a report that puts the failure first.
20
20
  - **Safe by default.** A case is read-only unless it lists environments under `writes`; writing anywhere except
@@ -100,7 +100,8 @@ exempts `@asrunjs/asrun` from pnpm's 24-hour release age, so a fresh release ins
100
100
  notes with `/make-case notes.md`. Fill in the steps, what is expected and the exact values.
101
101
  4. **Check it against the real pages.** `/explore-case header-logo staging` opens the pages (with your saved session),
102
102
  writes what they show into the case and turns what it cannot know into questions. Answer them in the case file, or
103
- in the UI's **Answer** sheet, and run it again.
103
+ in the UI's **Answer** sheet, and run it again. With many profiles, `--profiles a,b` opens only those; a later run
104
+ with the others adds theirs, and the case keeps both.
104
105
  5. **Generate, run, read.** `/gen-spec header-logo staging` writes `tests/header-logo.spec.ts` and fills the case's
105
106
  `## Values`. Then:
106
107
 
@@ -209,15 +209,20 @@ export function buildJobSpec(request) {
209
209
  if (request.attended && request.command !== 'explore-case') {
210
210
  throw new ActionError('Only explore-case can be attended.');
211
211
  }
212
- if ((request.profiles || request.hints !== undefined || request.report) && request.command !== 'heal-spec') {
213
- throw new ActionError('Only heal-spec takes profiles, hints and a report.');
212
+ if ((request.hints !== undefined || request.report) && request.command !== 'heal-spec') {
213
+ throw new ActionError('Only heal-spec takes hints and a report.');
214
+ }
215
+ // The spec is generated for every profile the case names (and runs for every profile), so a selection could only
216
+ // narrow it wrongly.
217
+ if (request.profiles && request.command === 'gen-spec') {
218
+ throw new ActionError('gen-spec takes no profiles: it uses every profile the case holds information for.');
214
219
  }
215
220
  const profilesInEnv = listProfiles(request.env, knownProfiles).map((profile) => profile.id);
216
221
  if (request.profiles?.some((id) => !profilesInEnv.includes(id))) {
217
222
  throw new ActionError(`Unknown profile for "${request.env}".`);
218
223
  }
219
224
  if (request.profiles?.length === 0)
220
- throw new ActionError('Choose at least one profile to heal.');
225
+ throw new ActionError('Choose at least one profile.');
221
226
  if (request.report !== undefined && !(REPORT_ID.test(request.report) && fs.existsSync(path.join(REPORTS_DIR, request.report, 'summary.json')))) {
222
227
  throw new ActionError(`Unknown report "${request.report}".`);
223
228
  }
@@ -234,7 +239,7 @@ export function buildJobSpec(request) {
234
239
  thinking: request.thinking,
235
240
  force: request.force,
236
241
  attended: request.attended,
237
- // Naming every profile is the same as leaving it out: the agent then takes all that are logged in.
242
+ // Naming every profile of the environment is the same as leaving it out: the agent then takes all that are logged in.
238
243
  profiles: request.profiles && request.profiles.length < profilesInEnv.length ? request.profiles : undefined,
239
244
  hints: hintsPath,
240
245
  report: request.report,
@@ -32,9 +32,12 @@ export interface AiRunOptions {
32
32
  attended?: boolean;
33
33
  /** Show the agent's reasoning in the log (`--thinking` for OpenCode). Off by default: it costs tokens and time. */
34
34
  thinking?: boolean;
35
- /** `heal-spec` only: the profiles to heal. Omitted = every profile that is logged in in `env`. */
35
+ /**
36
+ * `explore-case` and `heal-spec` only: the profiles to open / heal. Omitted = every profile that applies in `env`
37
+ * (all that are logged in, for heal). `gen-spec` takes none: it uses every profile the case names.
38
+ */
36
39
  profiles?: string[];
37
- /** `heal-spec` only: file (relative to the project root) with the user's description and clues. */
40
+ /** `heal-spec` only: file (relative to the project root) with what the user wants repaired or improved, and any clues. */
38
41
  hints?: string;
39
42
  /** `heal-spec` only: the report (folder name under reports/) to read first instead of the newest one. */
40
43
  report?: string;
@@ -48,7 +51,7 @@ export interface AiSession {
48
51
  id: string;
49
52
  resume: boolean;
50
53
  }
51
- /** The prompt of a one-shot run. `heal-spec` has no questions of its own, so it keeps the plain slash command. */
54
+ /** The prompt of a one-shot run. Every command may leave open questions in the case, so every one ends with a RESULT line. */
52
55
  export declare function headlessPrompt(command: AiCommand, caseId: string, options: AiRunOptions): string;
53
56
  /** Executable and arguments of the one-shot (non-interactive) run. */
54
57
  export declare function headlessInvocation(tool: AiTool, command: AiCommand, caseId: string, options: AiRunOptions, session?: AiSession): {
@@ -17,16 +17,18 @@ export const AI_COMMAND_WRITES = {
17
17
  * Agents never write data in any of them: a person does those steps.
18
18
  */
19
19
  export const AI_ENV_HINT = {
20
- 'explore-case': 'Where the agent explores. It only reads; with Attended on, you do the steps that write data.',
20
+ 'explore-case': 'Where the agent explores. Only the profiles you tick are opened; it only reads, and with Attended on you do the steps that write data.',
21
21
  'gen-spec': 'Where the agent explores and verifies the spec.',
22
- 'heal-spec': 'Where the failing spec is diagnosed. Only the profiles that are logged in there are run.',
22
+ 'heal-spec': 'Where the failure is diagnosed and the pages are checked. Only the profiles that are logged in there are run.',
23
23
  };
24
24
  export const slashCommand = (command, caseId, options) => {
25
25
  const { env, force, attended, profiles, hints, report } = options;
26
26
  const heal = command === 'heal-spec';
27
+ // gen-spec has no profile option: its scope is every profile the case names.
28
+ const picksProfiles = command === 'explore-case' || heal;
27
29
  return (`/${command} ${caseId} ${env}${force && command === 'gen-spec' ? ' --force' : ''}` +
28
30
  `${attended && command === 'explore-case' ? ' --attended' : ''}` +
29
- `${heal && profiles?.length ? ` --profiles ${profiles.join(',')}` : ''}` +
31
+ `${picksProfiles && profiles?.length ? ` --profiles ${profiles.join(',')}` : ''}` +
30
32
  `${heal && report ? ` --report ${report}` : ''}` +
31
33
  `${heal && hints ? ` --hints ${hints}` : ''}`);
32
34
  };
@@ -69,7 +71,9 @@ const STREAM_ARGS = {
69
71
  opencode: ['--format', 'json'],
70
72
  };
71
73
  function invocation(tool, prompt, writes, session, model, thinking) {
72
- // OpenCode creates the session when the id does not exist yet, and continues it when it does.
74
+ // OpenCode creates the session when the id does not exist yet, and continues it when it does. It also keeps the
75
+ // agent (build/plan) on the session, so a session started in plan mode would only propose changes and ask a question
76
+ // nobody can answer; every one-shot run is a build run, whatever the session it continues.
73
77
  // A model is named on every run: without one OpenCode falls back to its built-in free model, which has a daily
74
78
  // usage limit, and nothing in the CLI's own state (the TUI's last-used model) applies to a one-shot run.
75
79
  if (tool === 'opencode')
@@ -78,6 +82,8 @@ function invocation(tool, prompt, writes, session, model, thinking) {
78
82
  args: [
79
83
  'run',
80
84
  '--auto',
85
+ '--agent',
86
+ 'build',
81
87
  ...STREAM_ARGS.opencode,
82
88
  ...(session ? ['-s', session.id] : []),
83
89
  ...(model ? ['--model', model] : []),
@@ -121,11 +127,9 @@ const ATTENDED = 'Attended run: a person is at the machine and the browser windo
121
127
  'inside the call that runs the card: the web UI sees that a person is needed only from that call, so without it ' +
122
128
  'there is no "Your turn" badge and no notification. Nobody can answer questions in the chat, so write open ' +
123
129
  'questions into the case file as the skill describes and end with the RESULT: line.';
124
- /** The prompt of a one-shot run. `heal-spec` has no questions of its own, so it keeps the plain slash command. */
130
+ /** The prompt of a one-shot run. Every command may leave open questions in the case, so every one ends with a RESULT line. */
125
131
  export function headlessPrompt(command, caseId, options) {
126
132
  const slash = slashCommand(command, caseId, options);
127
- if (command === 'heal-spec')
128
- return slash;
129
133
  if (command === 'explore-case' && options.attended)
130
134
  return `${slash} ${ATTENDED}`;
131
135
  return `${slash} ${NON_INTERACTIVE}`;
@@ -103,7 +103,7 @@ export type StartJobRequest = {
103
103
  * its built-in free model. `heal-spec` keeps the model of the run it continues.
104
104
  */
105
105
  model?: string;
106
- /** `heal-spec` only: the profiles to heal; omitted = every profile that is logged in. */
106
+ /** `explore-case` and `heal-spec` only: the profiles to open / heal; omitted = every profile that applies. `gen-spec` takes none: it uses every profile the case names. */
107
107
  profiles?: string[];
108
108
  /** `heal-spec` only: what the user knows (optional). The server stores it as a file for the agent. */
109
109
  hints?: string;