@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.
- package/.claude/commands/explore-case.md +6 -2
- package/.claude/commands/gen-spec.md +4 -1
- package/.claude/commands/heal-spec.md +18 -7
- package/.claude/skills/exploring-cases/SKILL.md +19 -8
- package/.claude/skills/exploring-cases/references/open-questions.md +2 -2
- package/.claude/skills/generating-specs/SKILL.md +85 -30
- package/AGENTS.md +17 -9
- package/README.md +4 -3
- package/dist/ui/server/actions.js +9 -4
- package/dist/ui/shared/ai-commands.d.ts +6 -3
- package/dist/ui/shared/ai-commands.js +11 -7
- package/dist/ui/shared/types.d.ts +1 -1
- package/docs/ai-workflow.md +38 -11
- package/docs/theme-guide.html +93 -5
- package/docs/troubleshooting.md +7 -3
- package/docs/web-ui.md +28 -6
- package/package.json +1 -1
- package/templates/.claude/commands/explore-case.md +7 -2
- package/templates/.claude/commands/gen-spec.md +4 -1
- package/templates/.claude/commands/heal-spec.md +22 -12
- package/ui/dist/assets/{dist-ChBHX4rh.js → dist-B7HeglDd.js} +1 -1
- package/ui/dist/assets/{dist-CJiwxr1r.js → dist-B9_GXR2G.js} +1 -1
- package/ui/dist/assets/{dist-N-o4QIaZ.js → dist-Bb36rNUw.js} +1 -1
- package/ui/dist/assets/{dist-BpkTvixI.js → dist-DevASegc.js} +1 -1
- package/ui/dist/assets/{dist-DoChb6QQ.js → dist-DfBpjgZT.js} +1 -1
- package/ui/dist/assets/{dist-BgPbx2GZ.js → dist-Dq4Vnw5N.js} +1 -1
- package/ui/dist/assets/{dist-CavmZIv7.js → dist-Dqc_tfzC.js} +1 -1
- package/ui/dist/assets/{dist-Do_MhHKF.js → dist-G9KNeh6u.js} +1 -1
- package/ui/dist/assets/{dist-D6jmnDr-.js → dist-GvRUDL-2.js} +1 -1
- package/ui/dist/assets/{dist-BC_P1Gnf.js → dist-WvvCZeeu.js} +1 -1
- package/ui/dist/assets/index-BJ5D2mFK.js +346 -0
- package/ui/dist/assets/index-BXY-eKBn.css +2 -0
- package/ui/dist/help/case-explore.png +0 -0
- package/ui/dist/index.html +2 -2
- package/ui/dist/assets/index-Cy2l91qV.css +0 -2
- package/ui/dist/assets/index-DGQ5logJ.js +0 -338
package/docs/ai-workflow.md
CHANGED
|
@@ -8,16 +8,25 @@ so the procedure lives in one place and a project can refine it (see
|
|
|
8
8
|
| Command | What it does |
|
|
9
9
|
|---|---|
|
|
10
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. |
|
|
11
|
+
| `/explore-case <case-id> <env> [--attended] [--profiles a,b]` | Open the real pages and write what they show into the case. |
|
|
12
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>]` |
|
|
13
|
+
| `/heal-spec <case-id> <env> [--profiles a,b] [--report <run-id>] [--hints <file>]` | Repair a failing spec, or refine a case and its spec on request, from the last report and what you asked for. |
|
|
14
14
|
|
|
15
15
|
The environment is **required** by every command except `/make-case`, which does not open pages. There is no default.
|
|
16
16
|
|
|
17
|
+
`--profiles` exists on `/explore-case` and `/heal-spec`, not on `/gen-spec`: a spec runs once per profile in the
|
|
18
|
+
environment, so it is generated from every profile the case holds information for, never from a chosen subset.
|
|
19
|
+
|
|
17
20
|
Every command ends with `RESULT: ready` or `RESULT: needs-input`. `needs-input` means somebody has to answer a
|
|
18
21
|
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
22
|
it writes its questions into the case and stops.
|
|
20
23
|
|
|
24
|
+
A one-shot run is a **build** run: it writes the case or the spec, never a plan. AsRun starts OpenCode with
|
|
25
|
+
`--agent build`, because OpenCode keeps the agent (build or plan) on the session and a plan session would only propose
|
|
26
|
+
changes and ask a question nobody can answer. Whatever mode a run finds itself in, it still ends with the `RESULT:`
|
|
27
|
+
line: a run that was not allowed to write files (plan mode, a read-only sandbox) is `needs-input`, not a plan and a
|
|
28
|
+
question.
|
|
29
|
+
|
|
21
30
|
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
31
|
**Cases > From memo**. The UI runs the selected AI tool and streams the output into the Jobs bar.
|
|
23
32
|
|
|
@@ -55,6 +64,11 @@ publishes, shares or deletes anything, in staging or in production. It:
|
|
|
55
64
|
- lists the candidates instead of guessing a target (`## Targets`);
|
|
56
65
|
- edits the case file only (plus reference screenshots under `.explore/targets/`).
|
|
57
66
|
|
|
67
|
+
With many profiles, `--profiles a,b` opens only those (ids from `profiles.yaml`, comma-separated). The others are not
|
|
68
|
+
opened and nothing is written about them, so a case can be explored in passes: A and B first, C later. Runs add up; the
|
|
69
|
+
case keeps what the earlier runs recorded. In the web UI the profile picker of the toolbar does the same: every profile
|
|
70
|
+
that is logged in in that environment is ticked, and the list you tick is remembered for the next run there.
|
|
71
|
+
|
|
58
72
|
**To accept the suggestion, delete the whole block.** **To answer, write after `Answer:`.** Then run the command
|
|
59
73
|
again. In the web UI, the run's **What to do next** bar offers **Answer**: the question list, an answer box,
|
|
60
74
|
**Use the suggested defaults** and **Generate spec**. A continued job keeps the same conversation and permissions, and
|
|
@@ -92,8 +106,14 @@ Skip this for a `login: none` case. Sessions are per profile; see [profiles.md](
|
|
|
92
106
|
```
|
|
93
107
|
|
|
94
108
|
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
|
|
96
|
-
(`pnpm typecheck`, `pnpm case:check`; `pnpm spec:lint <case-id>` must have no failure).
|
|
109
|
+
`tests/header-logo.spec.ts`. It fills the case's `## Values` for the profiles the case holds information for and checks
|
|
110
|
+
the files (`pnpm typecheck`, `pnpm case:check`; `pnpm spec:lint <case-id>` must have no failure).
|
|
111
|
+
|
|
112
|
+
Its profile scope comes from the case, not from a selection: every profile id the case names — the keys of
|
|
113
|
+
`## Values`, a target card's `Reference <profile-id>:` line, an observation — plus `default`. That is why one run per
|
|
114
|
+
subset adds up: explore A and B first and C later, and `/gen-spec` covers all three. A profile the case says nothing
|
|
115
|
+
about runs on `default` alone, and the report names it. A profile that is not logged in is not dropped: its part is
|
|
116
|
+
written from the case and marked `not verified on the page`.
|
|
97
117
|
|
|
98
118
|
It does **not** run the spec and does **not** create screenshot baselines: running is a separate step (6).
|
|
99
119
|
|
|
@@ -125,7 +145,7 @@ profile. Baselines are the source of truth for pixel checks.
|
|
|
125
145
|
Then run all profiles and read the report: the video of each test, screenshots on failure, and the trace with the page
|
|
126
146
|
and the network requests. See [commands.md](./commands.md) for filters and baselines.
|
|
127
147
|
|
|
128
|
-
## Healing a
|
|
148
|
+
## Healing or refining a spec
|
|
129
149
|
|
|
130
150
|
```
|
|
131
151
|
/heal-spec header-logo staging
|
|
@@ -133,14 +153,15 @@ and the network requests. See [commands.md](./commands.md) for filters and basel
|
|
|
133
153
|
|
|
134
154
|
or **Heal spec** in the case toolbar, or **Heal this failure** on a failed result of the Reports page.
|
|
135
155
|
|
|
136
|
-
The
|
|
137
|
-
|
|
156
|
+
The command does two jobs: it repairs a spec that fails, and it refines a case and its spec when you ask for an
|
|
157
|
+
improvement. A passing last run is not a reason to refuse a request. It decides whether the test needs updating or
|
|
158
|
+
whether it found a real product problem, and it **never** edits an expectation just to make a failure disappear.
|
|
138
159
|
|
|
139
160
|
- The environment is required. Only the profiles that are logged in there run (`--logged-in` leaves the others out
|
|
140
161
|
instead of failing them).
|
|
141
|
-
- The web UI first opens a dialog: which profiles to
|
|
142
|
-
for what
|
|
143
|
-
|
|
162
|
+
- The web UI first opens a dialog: which profiles to run (every logged-in profile ticked) and an optional text box
|
|
163
|
+
for what should be repaired or improved, with evidence. The text is stored in `.tmp/hints/` and passed as `--hints`.
|
|
164
|
+
It guides the work; it never lowers the bar for a product finding.
|
|
144
165
|
- It reads the existing report first:
|
|
145
166
|
|
|
146
167
|
```bash
|
|
@@ -149,7 +170,13 @@ expectation just to make a failure disappear.
|
|
|
149
170
|
|
|
150
171
|
It prints status, failed step, error, call log and screenshots per profile, and whether the report is fresh or
|
|
151
172
|
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
|
|
173
|
+
a request has to be checked. It reads the pages (read-only) when a change has to be judged on them, and it never
|
|
174
|
+
performs a step that writes data. After editing the spec, it runs the case once to confirm.
|
|
175
|
+
- The case is the source of truth: a change to a step, an expectation, a value or a target is written into the case
|
|
176
|
+
first, then into the spec. A large change regenerates the spec from the case.
|
|
177
|
+
- **Review gate.** If the change leaves an open question or an unconfirmed observation in the case, healing stops
|
|
178
|
+
before the spec: you review the case, answer or accept what it asks, and run `/explore-case` again. The spec is
|
|
179
|
+
generated after that, not before.
|
|
153
180
|
|
|
154
181
|
## Why a generated spec can be trusted
|
|
155
182
|
|
package/docs/theme-guide.html
CHANGED
|
@@ -257,6 +257,29 @@
|
|
|
257
257
|
.side .item { padding: 8px 16px 8px 12px; border-bottom: 1px solid var(--line); border-left: 4px solid transparent; font-family: var(--mono); font-size: 14px; }
|
|
258
258
|
.side .item.selected { background: var(--theme-bg); border-left-color: var(--theme); }
|
|
259
259
|
|
|
260
|
+
/* Filter bar (mirror components/filter-chips.tsx and components/search-field.tsx). */
|
|
261
|
+
.filters { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; }
|
|
262
|
+
.filters .group-label { margin-right: 2px; font-size: 12px; color: var(--ink-3); }
|
|
263
|
+
.filter-chip {
|
|
264
|
+
display: inline-flex; align-items: center; gap: 4px; height: 24px; padding: 0 6px; border-radius: 2px;
|
|
265
|
+
background: var(--card); color: var(--ink-2); font-size: 12px; box-shadow: inset 0 0 0 1px var(--line-mid);
|
|
266
|
+
}
|
|
267
|
+
.filter-chip .count { color: var(--ink-3); }
|
|
268
|
+
.filter-chip.on {
|
|
269
|
+
background: var(--theme-btn); color: #fff; font-weight: 500;
|
|
270
|
+
box-shadow: inset 0 0 0 1px var(--theme-btn);
|
|
271
|
+
}
|
|
272
|
+
.filter-chip.on .count { color: rgb(255 255 255 / .75); }
|
|
273
|
+
.filter-chip.on.production { background: var(--jiang); box-shadow: inset 0 0 0 1px var(--jiang); }
|
|
274
|
+
.filter-chip.on.staging { background: var(--cheng); box-shadow: inset 0 0 0 1px var(--cheng); }
|
|
275
|
+
.filter-chip.on.other { background: var(--gold-deep); box-shadow: inset 0 0 0 1px var(--gold-deep); }
|
|
276
|
+
.filter-search { position: relative; width: 260px; max-width: 100%; }
|
|
277
|
+
.filter-search svg { position: absolute; left: 8px; top: 50%; transform: translateY(-50%); color: var(--ink-3); }
|
|
278
|
+
.filter-search .fake {
|
|
279
|
+
display: flex; align-items: center; height: 32px; padding: 0 8px 0 28px; border: 1px solid var(--line-mid);
|
|
280
|
+
border-radius: 4px; background: #fff; color: var(--ink-3); font-size: 14px;
|
|
281
|
+
}
|
|
282
|
+
|
|
260
283
|
.seal-stamp {
|
|
261
284
|
display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 3px;
|
|
262
285
|
background: var(--seal); color: var(--paper); font-family: var(--serif); font-weight: 400; font-size: 16px; line-height: 1;
|
|
@@ -302,6 +325,7 @@
|
|
|
302
325
|
<a href="#badges">Badges</a>
|
|
303
326
|
<a href="#action-colors">Action colors</a>
|
|
304
327
|
<a href="#dropdowns">Dropdowns</a>
|
|
328
|
+
<a href="#filter-bars">Filter bars</a>
|
|
305
329
|
<a href="#multi-value">Multi-value fields</a>
|
|
306
330
|
<a href="#icons">Icons</a>
|
|
307
331
|
<a href="#tooltips">Tooltips</a>
|
|
@@ -503,6 +527,7 @@
|
|
|
503
527
|
<tr><td><code>info</code> (light blue)</td><td>A view toggle that must be easy to find, not an action on data: "Full screen" / "Restore" in the Jobs drawer bar. Blue = read-only.</td></tr>
|
|
504
528
|
<tr><td><code>secondary</code> (white)</td><td>Everything else: Verify, Visit, Discard, Copy, navigation.</td></tr>
|
|
505
529
|
<tr><td><code>danger</code> (white, red text)</td><td>Cancel a running job, destructive confirmations.</td></tr>
|
|
530
|
+
<tr><td><code>invalid</code> (red field)</td><td>A control that is required and still empty, drawn as a button because it opens a panel instead of a native list (the environment picker of the case toolbar). The same red as an invalid dropdown; it is not an action.</td></tr>
|
|
506
531
|
</tbody>
|
|
507
532
|
</table>
|
|
508
533
|
<p class="small muted">Hover lifts the button 1px; disabled is 50% opacity with no shadow. With an icon, add <code>gap-1.5</code> to the button.</p>
|
|
@@ -522,7 +547,7 @@
|
|
|
522
547
|
<tbody>
|
|
523
548
|
<tr><td><span class="badge green">green</span></td><td>ok: Logged in</td><td>Succeeded</td></tr>
|
|
524
549
|
<tr><td><span class="badge red">red</span></td><td>expired, missing</td><td>Failed</td></tr>
|
|
525
|
-
<tr><td><span class="badge amber">amber</span></td><td>unverified, error</td><td>Needs your input (an AI run that ended with <code>RESULT: needs-input</code>)</td></tr>
|
|
550
|
+
<tr><td><span class="badge amber">amber</span></td><td>unverified, error</td><td>Needs your input (an AI run that ended with <code>RESULT: needs-input</code>) or No result (a run that printed no <code>RESULT:</code> line at all)</td></tr>
|
|
526
551
|
<tr><td><span class="badge blue">blue</span></td><td>-</td><td>Running</td></tr>
|
|
527
552
|
<tr><td><span class="badge gray">gray</span></td><td>-</td><td>Cancelled</td></tr>
|
|
528
553
|
</tbody>
|
|
@@ -584,7 +609,7 @@
|
|
|
584
609
|
<tbody>
|
|
585
610
|
<tr><td><code>explore-case</code></td><td>blue (qing)</td><td>Edits only the case file (wording, observations, open questions, inputs). The agent only reads pages; you do the steps that write data.</td></tr>
|
|
586
611
|
<tr><td><code>gen-spec</code></td><td>amber (gold)</td><td>Writes: creates the spec and fills the case's <code>## Values</code>. Does not run the tests.</td></tr>
|
|
587
|
-
<tr><td><code>heal-spec</code></td><td>red (seal)</td><td>
|
|
612
|
+
<tr><td><code>heal-spec</code></td><td>red (seal)</td><td>Repairs the spec or refines the case and the spec, then runs the tests.</td></tr>
|
|
588
613
|
</tbody>
|
|
589
614
|
</table>
|
|
590
615
|
<p class="small muted">
|
|
@@ -600,6 +625,14 @@
|
|
|
600
625
|
<code>Select</code> is a button that draws its own list under it, in a <code>card-popup</code> panel, which is what
|
|
601
626
|
gives every dropdown the same raised look.
|
|
602
627
|
</p>
|
|
628
|
+
<p class="small muted">
|
|
629
|
+
Two controls put their label inside the button and open a panel with a title rather than a bare list: the
|
|
630
|
+
environment picker of the case toolbar (<code>Env: staging</code>, <code>components/env-picker.tsx</code>) and the
|
|
631
|
+
profile picker beside it (<code>Profiles: 2 of 3</code>, <code>components/profile-picker.tsx</code>). They are
|
|
632
|
+
buttons because their list needs a title and a line of explanation, not a <code>Select</code>; they keep this
|
|
633
|
+
section's height, <code>card-popup</code> panel, keyboard and click-outside rules. Do not turn them back into a
|
|
634
|
+
<code>Select</code> that repeats the label on every row.
|
|
635
|
+
</p>
|
|
603
636
|
<div class="demo">
|
|
604
637
|
<span class="select"><span class="fake">staging</span><svg class="chev" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></span>
|
|
605
638
|
<span class="select focus"><span class="fake">staging</span><svg class="chev" xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg></span>
|
|
@@ -654,6 +687,60 @@
|
|
|
654
687
|
</tbody>
|
|
655
688
|
</table>
|
|
656
689
|
|
|
690
|
+
<h3 id="filter-bars">Filter bars</h3>
|
|
691
|
+
<p>
|
|
692
|
+
A list that can get long (the profiles of the workspace, the profiles of a case's Values table) gets a filter bar
|
|
693
|
+
above it: <code>SearchField</code> and <code>FilterChips</code> from <code>ui/web/src/components/</code>. They are
|
|
694
|
+
controls only; the page decides what a value means and filters its own rows.
|
|
695
|
+
</p>
|
|
696
|
+
<div class="demo" style="display:block">
|
|
697
|
+
<div class="filter-search" style="margin-bottom:8px">
|
|
698
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
|
|
699
|
+
<span class="fake">atpil</span>
|
|
700
|
+
</div>
|
|
701
|
+
<div style="display:flex;flex-wrap:wrap;gap:8px 16px">
|
|
702
|
+
<div class="filters">
|
|
703
|
+
<span class="group-label">Env</span>
|
|
704
|
+
<span class="filter-chip">All <span class="count">17</span></span>
|
|
705
|
+
<span class="filter-chip on production">PRODUCTION <span class="count">7</span></span>
|
|
706
|
+
<span class="filter-chip on staging">STAGING <span class="count">8</span></span>
|
|
707
|
+
<span class="filter-chip">SANDBOX <span class="count">2</span></span>
|
|
708
|
+
</div>
|
|
709
|
+
<div class="filters">
|
|
710
|
+
<span class="group-label">Tag</span>
|
|
711
|
+
<span class="filter-chip">All <span class="count">17</span></span>
|
|
712
|
+
<span class="filter-chip on">atp <span class="count">6</span></span>
|
|
713
|
+
<span class="filter-chip">compass <span class="count">4</span></span>
|
|
714
|
+
<span class="filter-chip">corp <span class="count">4</span></span>
|
|
715
|
+
</div>
|
|
716
|
+
</div>
|
|
717
|
+
<p class="small muted" style="margin:8px 0 0">Showing 6 of 17 profiles · <span style="text-decoration:underline">Clear filters</span></p>
|
|
718
|
+
</div>
|
|
719
|
+
<p class="small muted">
|
|
720
|
+
Search "atpil" with the dashes left out, both environments pressed and one tag: the two groups are combined with
|
|
721
|
+
AND.
|
|
722
|
+
</p>
|
|
723
|
+
<pre class="card mono" style="overflow:auto">import {SearchField} from '../components/search-field';
|
|
724
|
+
import {FilterChips} from '../components/filter-chips';
|
|
725
|
+
|
|
726
|
+
<SearchField value={query} onChange={setQuery} ariaLabel="Search profiles" placeholder="Search id, name..." />
|
|
727
|
+
<FilterChips label="Env" selected={envFilter} onChange={setEnvFilter} allCount={profiles.length}
|
|
728
|
+
options={envs.map((env) => ({value: env, label: env.toUpperCase(), count: countOf(env), tone: envFillClass(env)}))} /></pre>
|
|
729
|
+
<table>
|
|
730
|
+
<thead><tr><th>Part</th><th>Rule</th></tr></thead>
|
|
731
|
+
<tbody>
|
|
732
|
+
<tr><td>Search</td><td>One <code>SearchField</code> at the top. It looks in id, name, account, environment, tags and address; every word of the query must match one of them. A compact copy without <code>-</code>, <code>_</code>, <code>.</code> or spaces lets <code>atpil</code> find <code>atp-il-staging</code>.</td></tr>
|
|
733
|
+
<tr><td>Groups</td><td>One <code>FilterChips</code> per kind of value (environment, tag). Inside a group a row matches <b>any</b> pressed value; the groups are combined with <b>AND</b>. An empty group filters nothing.</td></tr>
|
|
734
|
+
<tr><td>All</td><td>The first chip of a group clears that group only. It is pressed while the group has no selection, so "no filter" can always be told from "everything selected".</td></tr>
|
|
735
|
+
<tr><td>Counts</td><td>Each option shows how many rows it would leave, and <code>All</code> shows the group's total. Counts come from the unfiltered list, so they do not dance while a value is pressed.</td></tr>
|
|
736
|
+
<tr><td>Pressed look</td><td>Neutral chips take the page accent (<code>theme-btn</code>, white text). Environment chips pass <code>envFillClass(env)</code>, the same solid marker as <code>EnvBadge</code>, so color means the environment here too.</td></tr>
|
|
737
|
+
<tr><td>State</td><td>The chip is the button: <code>aria-pressed</code>, a tooltip with its name (and "pressed" while on), and the count as a second, quieter part of the label.</td></tr>
|
|
738
|
+
<tr><td>Feedback</td><td>While a filter is active, show <code>Showing N of M</code> and <b>Clear filters</b>. When nothing matches, the empty state repeats the clear action instead of leaving a blank table.</td></tr>
|
|
739
|
+
<tr><td>Not remembered</td><td>A filter is a way of looking, not a setting: do not put it in <code>localStorage</code>. A reload shows everything, so nothing looks missing after lunch.</td></tr>
|
|
740
|
+
<tr><td>In the case panel</td><td>The Values table adds a two-button <b>Rows: Keys / Profiles</b> switch beside the search as soon as a case names two profiles, and shows the search and the environment chips only when it names more than three. The filters and the switch belong to that case; opening another case starts unfiltered.</td></tr>
|
|
741
|
+
</tbody>
|
|
742
|
+
</table>
|
|
743
|
+
|
|
657
744
|
<h2 id="multi-value">Multi-value fields</h2>
|
|
658
745
|
<p>
|
|
659
746
|
A field whose value is a list is never a <a href="#dropdowns">Dropdown</a> plus an "Add" text input: that mixes a
|
|
@@ -829,11 +916,12 @@
|
|
|
829
916
|
</p>
|
|
830
917
|
<p class="muted">
|
|
831
918
|
The preview block that holds the editor caret is shaded with <code>theme-bg</code> at 60% and a 2px <code>theme</code> left
|
|
832
|
-
border (an overlay, so it follows the page theme). The
|
|
833
|
-
|
|
919
|
+
border (an overlay, so it follows the page theme). The overlay paints <b>behind</b> the text, never over it: the
|
|
920
|
+
highlighted block keeps its own text color instead of looking washed out. The editor and the preview scroll together
|
|
921
|
+
and a click in the preview moves the caret; the logic is in <code>ui/web/src/lib/preview-sync.ts</code>.
|
|
834
922
|
</p>
|
|
835
923
|
<p class="muted">
|
|
836
|
-
The previous / next buttons of the
|
|
924
|
+
The previous / next buttons at the right end of the editor toolbar move the caret to the next marker and blink that marker for a moment in
|
|
837
925
|
both panes (<code>cm-kw-flash</code> in the editor, <code>kw-flash</code> over the <code><mark></code> of the
|
|
838
926
|
document): <code>gold-glow</code> at 65% behind it and a 2px <code>dai-deep</code> ring with a wider
|
|
839
927
|
<code>gold-glow</code> glow around it, the ring blinking on and off three times (0.45 s each), then it goes away.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -69,6 +69,10 @@ is recorded.
|
|
|
69
69
|
checkout has the card but not the screenshot. Run the exploration again on this machine (or look at the card and the
|
|
70
70
|
report's baseline).
|
|
71
71
|
|
|
72
|
+
**`/explore-case` takes long with many profiles** — pass `--profiles a,b` (the toolbar's profile picker does the same):
|
|
73
|
+
only those profiles are opened, and a later run with the others adds what it finds. `/gen-spec` has nothing to pick: it
|
|
74
|
+
covers every profile the case names.
|
|
75
|
+
|
|
72
76
|
## Installing
|
|
73
77
|
|
|
74
78
|
**`pnpm add` installs the previous release instead of `latest`** — pnpm holds back versions published in the last
|
|
@@ -131,6 +135,6 @@ read it. `doctor` lists the overrides it sees.
|
|
|
131
135
|
project. Add `--reset-rules` to replace them with the template, and expect to lose the project's rules.
|
|
132
136
|
|
|
133
137
|
**The AI did something you did not expect** — exploring and generating only read pages and edit the case file; healing
|
|
134
|
-
edits the spec and runs
|
|
135
|
-
needs a person's confirmation). None of them writes product data on its own, and none updates a
|
|
136
|
-
needs a person to write, run it with `--attended`; that is where a person acts, in the browser.
|
|
138
|
+
edits the case and the spec and runs the spec under the same write gate as any run (staging writes are allowed, any
|
|
139
|
+
other environment needs a person's confirmation). None of them writes product data on its own, and none updates a
|
|
140
|
+
baseline. If a step needs a person to write, run it with `--attended`; that is where a person acts, in the browser.
|
package/docs/web-ui.md
CHANGED
|
@@ -36,6 +36,10 @@ The UI's look is the **Inkstone** theme; the palette, buttons, badges and icons
|
|
|
36
36
|
|
|
37
37
|
One row per profile, with the same login check as `pnpm status`.
|
|
38
38
|
|
|
39
|
+
- **Filters** above the table: a search over id, name, account, environment, tags and address (a word may leave out
|
|
40
|
+
the dashes, so `atpil` finds `atp-il-staging`), plus multi-select chips for environments and tags. Every chip shows
|
|
41
|
+
how many profiles it leaves; the bar shows `Showing N of M profiles` and **Clear filters** while it is active. The
|
|
42
|
+
filters are not remembered, so a reload shows every profile again.
|
|
39
43
|
- **Log in** starts `pnpm signin <profile-id>`: log in in the browser window that opens, then press **Done, save the
|
|
40
44
|
session** on the card in that window (the job's "I've logged in, save the session" button does the same). A
|
|
41
45
|
logged-in profile shows `Login YES`.
|
|
@@ -53,7 +57,7 @@ One row per profile, with the same login check as `pnpm status`.
|
|
|
53
57
|
|
|
54
58
|
The case list is grouped by folder and collapses. The editor opens in **Single** with the **Panel** full width; the
|
|
55
59
|
toolbar switches to **Split** (source left, one pane right; drag the divider to resize, double-click to reset) or, in
|
|
56
|
-
Single,
|
|
60
|
+
Single, between **Panel**, **Editor** and **Doc**.
|
|
57
61
|
|
|
58
62
|
- **Source and preview** scroll together, the preview block at the caret is shaded, and a click in the preview puts
|
|
59
63
|
the caret on that line. `Ctrl/Cmd+S` saves.
|
|
@@ -66,7 +70,11 @@ Single, to **Editor** or **Doc**.
|
|
|
66
70
|
answer and the AI folds it in). Then the front matter (title, login, suites, writes and its note, environments), the
|
|
67
71
|
steps (sentence, `[writes]` / `[input]` / `[email]` tags, `- Ready when:` lines, `- Recipe:` lines), the
|
|
68
72
|
`## Expected` and `## Visual checks` bullets (drag to reorder), the `## Values` table (one column per profile; an
|
|
69
|
-
empty cell inherits `default`), and the `## Targets` cards read-only, with what each is missing.
|
|
73
|
+
empty cell inherits `default`), and the `## Targets` cards read-only, with what each is missing. With two or more
|
|
74
|
+
profiles the Values section offers a **Rows: Keys / Profiles** switch, and from four profiles on it also adds a
|
|
75
|
+
search and environment chips. With more profiles than keys it starts as one row per profile, so every name and
|
|
76
|
+
environment stays readable instead of being squeezed into a narrow column. Both the header and the profile/key
|
|
77
|
+
column stay in place while the table scrolls. `[T1]` chips open
|
|
70
78
|
the card: what it is, where, how it is identified, what it must not be, and its reference screenshots. Every section
|
|
71
79
|
collapses and has a **?** with a short help card. Panel edits land in the same draft as the source editor, so one
|
|
72
80
|
save covers both, and **Discard** throws them away together.
|
|
@@ -76,7 +84,9 @@ Single, to **Editor** or **Doc**.
|
|
|
76
84
|
the spec is rewritten. **Delete** (type the id, or fill it in with the feather icon) removes the case, its spec and
|
|
77
85
|
its baselines; both dialogs list exactly what they touch, and are refused while an AI or test run is in progress.
|
|
78
86
|
- **Toolbar:** command (explore / generate / heal, explore by default), **Environment** (required, no default),
|
|
79
|
-
**
|
|
87
|
+
**Profiles** (explore only: every profile that is logged in is ticked, the choice is remembered per environment, and
|
|
88
|
+
only the ticked ones are opened), **Attended** (only for explore, ticked by default), **Run AI**, copy the shell
|
|
89
|
+
command, open the AI guide.
|
|
80
90
|
- **From memo** opens a Markdown editor with a preview for raw notes. **Make cases** saves them to `.tmp/memos/` and
|
|
81
91
|
runs `/make-case` one-shot (defaults for gaps, `TODO` for unknown values, no page exploration, existing cases never
|
|
82
92
|
overwritten); **Copy /make-case** copies the memo for the interactive dialogue. When the run ends, **What to do
|
|
@@ -117,9 +127,21 @@ cancelled there. Opening it shrinks the page instead of covering it.
|
|
|
117
127
|
`.tmp/ui-jobs/`.
|
|
118
128
|
- When an AI job ends, a thin **What to do next** bar appears under the log (or **Details** opens it in a sheet):
|
|
119
129
|
open questions with an answer box and **Use the suggested defaults**, the confirmations the exploration marked, and
|
|
120
|
-
the next action (**Generate spec**, **Open Run tests**). A run that ends with
|
|
121
|
-
your input* badge; sending answers continues the same conversation, once per turn
|
|
122
|
-
not restarted. After a restart, answer in the case file and run the command again.
|
|
130
|
+
the next action (**Generate spec**, **Open Run tests**, **View spec**, **Re-explore**). A run that ends with
|
|
131
|
+
`needs-input` gets an amber *Needs your input* badge; sending answers continues the same conversation, once per turn
|
|
132
|
+
and only while the UI server has not restarted. After a restart, answer in the case file and run the command again.
|
|
133
|
+
- A run that ends without its `RESULT:` line gets an amber *No result* badge and a banner under the command instead of
|
|
134
|
+
a "what to do next" bar: nothing is known about what it did, so read the log before trusting the case or the spec.
|
|
135
|
+
A run that is not allowed to write files (plan mode, a read-only sandbox) ends that way; the framework starts
|
|
136
|
+
OpenCode with `--agent build` so that a one-shot run is never a plan.
|
|
137
|
+
- A **Generate spec** or **Re-explore** button opens the case with its Run AI button already on that command, in the
|
|
138
|
+
case view: the button gets a dark ring and an amber pulse, and a gold strip under the toolbar says what is still
|
|
139
|
+
missing (an environment, a save), until the run starts or you pick another command.
|
|
140
|
+
- After **Generate spec**, **View spec** opens the case on the spec editor. After **Heal spec**, the bar says either
|
|
141
|
+
that the case and the spec are up to date (with **View spec**), or that the case changed and needs your review: then
|
|
142
|
+
it counts the must-answer questions, suggestions and lines to confirm, and offers **Review the questions** /
|
|
143
|
+
**Review the marked lines** and **Re-explore** — healing does not generate a spec from a case that still asks
|
|
144
|
+
something.
|
|
123
145
|
|
|
124
146
|
## Help and AI guide
|
|
125
147
|
|
package/package.json
CHANGED
|
@@ -8,15 +8,20 @@ Rules combine in this order, and a later one wins where they conflict: the frame
|
|
|
8
8
|
`{{framework}}/AGENTS.md`, the framework skill, `.claude/skills/exploring-cases.md` if it exists, then the workspace
|
|
9
9
|
`AGENTS.md` §2 (Project rules). F1–F6 always win. Project facts are in `AGENTS.md` §3.
|
|
10
10
|
|
|
11
|
-
Arguments: `<case-id> <env> [--attended]`. The case id is the path below `cases/` without `.md` (it may include a
|
|
11
|
+
Arguments: `<case-id> <env> [--attended] [--profiles a,b]`. The case id is the path below `cases/` without `.md` (it may include a
|
|
12
12
|
group folder, e.g. `documents/landing-page`) and is required. `<env>` is `staging` or `production`; it is required and there
|
|
13
13
|
is no default (without one, stop and say so). With `--attended` I am at the machine and the browser window is
|
|
14
14
|
visible: when a step writes data or needs a choice, hand it to me as the skill describes, wait for me, and record
|
|
15
15
|
what I did as a general recipe under the step it belongs to (`- Recipe:` lines). Without `--attended`, never wait for me.
|
|
16
|
+
`--profiles a,b` explores only those profiles (comma-separated ids from `profiles.yaml`); without it, at least one
|
|
17
|
+
logged-in profile per domain. A profile the list leaves out is not opened and nothing is written about it: runs add up,
|
|
18
|
+
so a later run covers the rest.
|
|
16
19
|
|
|
17
20
|
Follow `{{framework}}/.claude/skills/exploring-cases/references/assertion-targets.md` (list the candidates, never
|
|
18
21
|
pick when in doubt, ask for the exact image/text/place/UI) and `readiness.md` (wait for the key requests and the
|
|
19
22
|
content before you read anything) of the skill.
|
|
20
23
|
|
|
21
24
|
This command edits `cases/<case-id>.md` only (plus screenshots under `.explore/targets/`). Do not ask me questions in
|
|
22
|
-
the chat: write open questions into the case file as the skill describes, and end with the `RESULT:` line.
|
|
25
|
+
the chat: write open questions into the case file as the skill describes, and end with the `RESULT:` line. If you are
|
|
26
|
+
not allowed to write files at all (a plan-mode run, a read-only sandbox), say what blocked you and end with
|
|
27
|
+
`RESULT: needs-input`.
|
|
@@ -12,10 +12,13 @@ Rules combine in this order, and a later one wins where they conflict: the frame
|
|
|
12
12
|
Arguments: `<case-id> [<env>] [--force]`. The case file is `cases/<case-id>.md` (the id may include one group folder,
|
|
13
13
|
e.g. `documents/landing-page`). `<env>` is the environment to explore in (`staging` or `production`); there is no default. If
|
|
14
14
|
the spec already exists, it is left alone unless `--force` is given; with `--force` it is regenerated and overwritten.
|
|
15
|
+
There is no profile option: the spec is generated for every profile the case holds information for (its `## Values`
|
|
16
|
+
keys, its target cards and its observations), not for a chosen subset and not only for the profiles that are logged in.
|
|
15
17
|
|
|
16
18
|
Follow the skill's procedure exactly, including its `assertion-targets.md` and `readiness.md`; run
|
|
17
19
|
`pnpm spec:lint <case-id>`. Only generate the spec: do not run `pnpm asrun` (running specs is a separate
|
|
18
20
|
process), and do not create baselines.
|
|
19
21
|
|
|
20
22
|
Do not ask me questions: resolve ambiguity as the skill describes (explore the page, use observed values marked for
|
|
21
|
-
QA, write the remaining questions into the case file), and end with the `RESULT:` line.
|
|
23
|
+
QA, write the remaining questions into the case file), and end with the `RESULT:` line. If you are not allowed to write
|
|
24
|
+
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:
|
|
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 framework skill `{{framework}}/.claude/skills/generating-specs/SKILL.md`, section "Healing a
|
|
5
|
+
Load the framework skill `{{framework}}/.claude/skills/generating-specs/SKILL.md`, section "Healing and refining a
|
|
6
6
|
spec", and follow it exactly.
|
|
7
7
|
|
|
8
8
|
Rules combine in this order, and a later one wins where they conflict: the framework baseline
|
|
@@ -13,19 +13,29 @@ Arguments: $ARGUMENTS
|
|
|
13
13
|
Format: `<case-id> <env> [--profiles a,b] [--report <run-id>] [--hints <file>]`, with optional free text after them.
|
|
14
14
|
- `<case-id>` and `<env>` (`staging` or `production`) are required. There is no default environment. Without one,
|
|
15
15
|
stop and say so.
|
|
16
|
-
- `--profiles`: the profiles to
|
|
16
|
+
- `--profiles`: the profiles to run when evidence is needed. Without it, every profile that is logged in in `<env>`.
|
|
17
17
|
- `--report`: the report (folder name under `reports/`) to read first. Without it, the newest report of this case.
|
|
18
|
-
- `--hints <file>`: a file with what the user
|
|
19
|
-
after the arguments is the same kind of
|
|
18
|
+
- `--hints <file>`: a file with what the user wants repaired or improved, with any evidence. Read it first. Any free
|
|
19
|
+
text after the arguments is the same kind of request. Both are optional.
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
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
|
|
22
|
+
product finding to make a test pass.
|
|
23
|
+
|
|
24
|
+
Do not ask me questions in the chat: this may run unattended. Resolve what the report, the case and the page can
|
|
25
|
+
resolve, and write what still needs a person into the case as a `[blocking]` question. End with the `RESULT:` line:
|
|
26
|
+
`ready` when the healing or the refinement is finished, `needs-input` when something is still open. If you could not
|
|
27
|
+
write files at all (a plan-mode run, a read-only sandbox, a refused permission), say in one sentence what blocked you
|
|
28
|
+
and end with `RESULT: needs-input`: never answer with a plan and a question instead.
|
|
29
|
+
|
|
30
|
+
Before you decide "product or test", audit the assertion itself (target card, baseline content, readiness): see step 5
|
|
31
|
+
of the skill's healing section, `assertion-targets.md` and `readiness.md`. Run `pnpm spec:lint <case-id>`.
|
|
23
32
|
Read the existing report first (`pnpm heal:evidence --case <case-id> --env <env>`), and run the case again only when
|
|
24
|
-
the skill says it is needed. Always run with `--logged-in`.
|
|
25
|
-
|
|
33
|
+
the skill says it is needed. Always run with `--logged-in`. If the change leaves an open question or an unconfirmed
|
|
34
|
+
marker in the case, stop there, tell me to review the case and run `/explore-case`, and end with
|
|
35
|
+
`RESULT: needs-input`. Otherwise tell me whether it is a test problem or a product finding.
|
|
26
36
|
|
|
27
37
|
Run `pnpm asrun ...`, `pnpm heal:evidence ...`, `pnpm spec:lint ...`, `pnpm baselines:review ...` and
|
|
28
38
|
`pnpm status ...` as plain commands, optionally piped to `tail` or `head`, and do not chain other commands with them
|
|
29
|
-
(`;` or `&&`). Only these exact forms run outside Claude Code's sandbox, which tsx and the browser need. If a
|
|
30
|
-
|
|
31
|
-
|
|
39
|
+
(`;` or `&&`). Only these exact forms run outside Claude Code's sandbox, which tsx and the browser need. If a command
|
|
40
|
+
still fails with `listen EPERM` or a sandbox error, stop and report it. Do not ask to disable the sandbox, and never
|
|
41
|
+
add `--allow-writes` or set `ASRUN_ALLOW_WRITES`.
|