ccqa 1.9.0 → 1.10.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/README.md CHANGED
@@ -2,19 +2,82 @@
2
2
 
3
3
  **Your Claude subscription already includes a QA engineer.**
4
4
 
5
- ccqa turns Claude Code into a browser test recorder and runner:
5
+ ccqa turns Claude Code into a browser test recorder and runner. You write a
6
+ test spec in YAML; Claude drives a real browser **once** to discover the
7
+ route; ccqa compiles that recording into ordinary test code your CI replays
8
+ with no model in the loop.
9
+
10
+ Recording is where the subscription pays off — `claude` on your machine is
11
+ enough, no extra API key. CI is where it stops needing one: a recorded spec
12
+ replays as plain test code. Only the optional Claude-driven parts —
13
+ [failure analysis](#failure-analysis-and-drift), [drift](#failure-analysis-and-drift),
14
+ [change selection](#wire-it-into-ci), and `mode: live` specs — need a
15
+ credential in CI.
6
16
 
7
- 1. Write a test spec in YAML — plain steps and expected results.
8
- 2. Claude drives a real browser **once** to discover the route
9
- (`ccqa record`).
10
- 3. ccqa compiles the recording into runnable test code for your `target:`
11
- — vitest replay, plain Playwright, or a runn runbook.
12
- 4. `ccqa run` replays everything into one report you can push to a
13
- shared hub.
17
+ [日本語版 README](./docs/README.ja.md)
14
18
 
15
- No extra API key. Just `claude`.
19
+ ## Install
16
20
 
17
- [日本語版 README](./docs/README.ja.md)
21
+ ```bash
22
+ pnpm add -D ccqa vitest agent-browser
23
+ ```
24
+
25
+ Requires Node.js **20+**.
26
+ [agent-browser](https://github.com/vercel-labs/agent-browser) and
27
+ [vitest](https://vitest.dev) are peer dependencies of the **default
28
+ agent-browser target** — they run its recorded tests. A project that only uses
29
+ an external target (`playwright`, `runn`) needs just `ccqa` plus that tool
30
+ (e.g. `pnpm add -D ccqa @playwright/test`); ccqa executes it through the
31
+ target's `runCommand`.
32
+
33
+ ## Quick start
34
+
35
+ **1. Write a spec** — by hand, or interactively with
36
+ [`ccqa draft`](./docs/draft.md). (`ccqa init` scaffolds the `.ccqa/`
37
+ skeleton.)
38
+
39
+ ```yaml
40
+ # .ccqa/features/tasks/test-cases/create-and-complete/spec.yaml
41
+ title: Create a task and mark it complete
42
+
43
+ steps:
44
+ - instruction: |
45
+ Open ${APP_URL}/login. Fill in email and password, submit the form.
46
+ expected: Redirected to /dashboard, user avatar visible in the header
47
+
48
+ - instruction: |
49
+ Click "New Task", fill in the title "Fix login bug", save.
50
+ expected: Task appears in the task list with status "Open"
51
+ ```
52
+
53
+ **2. Tell ccqa what `${APP_URL}` is.** A spec names variables instead of
54
+ embedding an environment, so the same spec runs against local and staging. A
55
+ `.env` file covers you locally; in CI the values come from a hub
56
+ (`ccqa hub var set`) so nothing environment-specific lives in the repo. See
57
+ [Profiles and environment variables](./docs/running.md#profiles-and-environment-variables).
58
+
59
+ ```bash
60
+ echo 'APP_URL=http://localhost:3000' >> .env
61
+ ```
62
+
63
+ **3. Record once** — Claude drives the browser and generates the test:
64
+
65
+ ```bash
66
+ ccqa record tasks/create-and-complete
67
+ ```
68
+
69
+ **4. Run it** — vitest replays the recording; no LLM involved:
70
+
71
+ ```bash
72
+ ccqa run tasks/create-and-complete
73
+ ```
74
+
75
+ A `report.json` (+ step screenshots) is always written to `ccqa-report/`.
76
+ See [Running specs](./docs/running.md) for flags and the report format.
77
+
78
+ If the spec sits behind a login that a recording cannot reproduce — an SSO
79
+ redirect, a device-trust gate — record a session by hand once with
80
+ [`ccqa session bootstrap`](./docs/sessions.md) and name it in the spec.
18
81
 
19
82
  ## How it works
20
83
 
@@ -48,101 +111,196 @@ into**; every target is the same deterministic replay:
48
111
 
49
112
  `runCommand` is the one-line command your repo already uses to run that
50
113
  tool, declared once in `.ccqa/config.yaml` — e.g.
51
- `pnpm exec playwright test {files}`. ccqa substitutes the spec's generated
52
- test files for `{files}` and a per-spec artifacts directory for
53
- `{artifactsDir}`; see [Generation targets](./docs/targets.md) for the full
54
- contract.
114
+ `pnpm exec playwright test {files}`. See
115
+ [Generation targets](./docs/targets.md) for the substitution contract.
55
116
 
56
117
  **Live (`mode: live`).** No codegen: Claude drives every run and judges
57
118
  each step's `expected` — for fragile, timing-heavy UIs where a fixed
58
119
  recording would break.
59
120
 
60
- Either way and whatever the `target:` — opting in with
61
- `ccqa run --failure-analysis [base]` gives every failing spec a root-cause
62
- call (TEST_DRIFT / SPEC_CHANGE / PRODUCT_BUG) against the source diff since
63
- `[base]`, gradable on the hub — and the hub learns from your grades.
121
+ ## Failure analysis and drift
64
122
 
65
- ## Install
123
+ A failing E2E test does not say whose problem it is. ccqa answers that
124
+ question in one vocabulary, from two directions.
125
+
126
+ **When a spec fails**, `ccqa run --failure-analysis [base]` labels the cause
127
+ — `TEST_DRIFT`, `SPEC_CHANGE`, `PRODUCT_BUG`, or `UNKNOWN` when the evidence
128
+ does not support a call. The label comes with a drift audit of the same spec,
129
+ because "did the test break" and "does the test still describe the product"
130
+ are the same investigation. `[base]` is what the diff is read against: a git
131
+ ref, or `last-green` to have each spec diff against the commit where it last
132
+ passed. With neither, the label rests on the failure alone and says so.
133
+
134
+ **Before anything runs**, `ccqa drift` asks the second question on its own,
135
+ with no browser: does each spec still describe the code? For a deterministic
136
+ spec that means both artifacts — the spec a human wrote and the test code
137
+ compiled from it — since either can fall out of step. Which one drifted
138
+ decides the repair, so the audit reports it: stale generated code is
139
+ re-recorded, a stale spec needs a human.
140
+
141
+ Every call is gradable on the hub, and the hub learns from your grades. See
142
+ [Failure triage](./docs/running.md#failure-triage) and
143
+ [Drift detection](./docs/running.md#drift-detection).
144
+
145
+ ## The hub
146
+
147
+ A hub is optional for one person on one machine. For a team, or for CI, it is
148
+ where the shared state lives — there is no second place to put it:
149
+
150
+ - the coverage inventory of what is tested
151
+ ([perspectives](./docs/spec.md#inventory-coverage-with-perspectives)), kept
152
+ current by `record`/`generate`
153
+ - the variables `${…}` resolve to, and saved browser sessions, fetched at run
154
+ time — so CI holds one secret instead of an environment
155
+ - the deploy log behind `--changed=last-run`, and the drift ledger
156
+ - a dashboard of runs with per-step screenshots, triage grading, and the
157
+ prompts learned from those grades
66
158
 
67
159
  ```bash
68
- pnpm add -D ccqa vitest agent-browser
160
+ export CCQA_HUB_TOKEN=$(openssl rand -hex 24)
161
+ export CCQA_HUB_ENCRYPTION_KEY=$(openssl rand -hex 32) # required to store
162
+ ccqa serve # sessions/variables
69
163
  ```
70
164
 
71
- Requires Node.js **20+**.
72
- [agent-browser](https://github.com/vercel-labs/agent-browser) and
73
- [vitest](https://vitest.dev) are peer dependencies of the **default
74
- agent-browser target** they run its recorded tests. A project that only uses
75
- an external target (`playwright`, `runn`) needs just `ccqa` plus that tool
76
- (e.g. `pnpm add -D ccqa @playwright/test`); ccqa executes it through the
77
- target's `runCommand`.
165
+ The repository root also ships a `Dockerfile` and `docker-compose.yaml` for
166
+ container deployment — clone it, or copy them from
167
+ [Running the hub in a container](./docs/hub.md#running-the-hub-in-a-container);
168
+ they are not part of the npm package.
78
169
 
79
- ## Quick start
170
+ See [Hub](./docs/hub.md) for the full setup and
171
+ [Hub API](./docs/hub-api.md) to script it over HTTP.
80
172
 
81
- **1. Write a spec** — by hand, or interactively with
82
- [`ccqa draft`](./docs/draft.md). (`ccqa init` scaffolds the `.ccqa/`
83
- skeleton.)
173
+ ## Wire it into CI
84
174
 
85
- ```yaml
86
- # .ccqa/features/tasks/test-cases/create-and-complete/spec.yaml
87
- title: Create a task and mark it complete
175
+ Three jobs. They are independent: the pull-request job on its own is a
176
+ complete adoption, and the other two can come later.
88
177
 
89
- steps:
90
- - instruction: |
91
- Open ${APP_URL}/login. Fill in email and password, submit the form.
92
- expected: Redirected to /dashboard, user avatar visible in the header
178
+ | Job | Trigger | Question it answers |
179
+ |---|---|---|
180
+ | Pre-merge run | `pull_request` | Does this change break a spec, and whose fault is it? |
181
+ | Post-deploy run | after a deploy | Which specs' last result is no longer trustworthy? |
182
+ | Drift audit | `schedule` | Do the specs still describe the code? |
93
183
 
94
- - instruction: |
95
- Click "New Task", fill in the title "Fix login bug", save.
96
- expected: Task appears in the task list with status "Open"
184
+ All three need two things:
185
+
186
+ - **A Claude credential.** Replaying a recorded spec uses no model, but the
187
+ change selection, the failure analysis and the audit all do.
188
+ - **A running [hub](#the-hub)**, reached with `CCQA_HUB_URL` and
189
+ `CCQA_HUB_TOKEN`. Only a pre-merge run with no `--profile` and no
190
+ `--push-report` can do without one.
191
+
192
+ See [Environment variables](./docs/commands.md#environment-variables) for the
193
+ full list.
194
+
195
+ A **profile** is one deployed environment. It names a bucket of variables and
196
+ saved sessions on the hub, and — since two environments sit at different
197
+ commits — its own deploy history. Register the variables your specs reference
198
+ once, from your machine:
199
+
200
+ ```bash
201
+ ccqa hub var set APP_URL --value https://app.example --profile staging
97
202
  ```
98
203
 
99
- **2. Record once** Claude drives the browser and generates the test:
204
+ Pass the same `--profile` and `--project` in every job. That is what makes the
205
+ jobs refer to the same environment.
206
+
207
+ ### On a pull request
208
+
209
+ Run the specs the change reaches, and label what broke.
100
210
 
101
211
  ```bash
102
- ccqa record tasks/create-and-complete
212
+ ccqa run --changed --failure-analysis --profile staging \
213
+ --format github --push-report
103
214
  ```
104
215
 
105
- **3. Run it** vitest replays the recording; no LLM involved:
216
+ - `--changed` selects the specs the diff reaches. A spec it cannot clear runs
217
+ anyway.
218
+ - `--failure-analysis` labels the cause of each failure.
219
+ - `--profile staging` fetches that environment's variables and saved sessions
220
+ from the hub. Without it, a spec's `${…}` references go unresolved.
221
+ - `--format github` annotates the pull request.
222
+ - `--push-report` streams results to the hub as the run executes.
223
+
224
+ **Set `fetch-depth: 0` on `actions/checkout`.** Both selection flags read
225
+ their baseline from `GITHUB_BASE_REF` and resolve it against `origin/<base>`,
226
+ which a shallow checkout does not have. Without it the run exits with a usage
227
+ error before the first test. Outside a `pull_request` workflow there is no
228
+ `GITHUB_BASE_REF`, so pass the base yourself: `--changed=origin/main`.
229
+
230
+ `--dry-run` prints the selection and stops. The selection costs one model call
231
+ either way.
232
+
233
+ ### On a deploy
234
+
235
+ Two steps, in two jobs. First, when the deploy succeeds, tell the hub what
236
+ shipped:
106
237
 
107
238
  ```bash
108
- ccqa run tasks/create-and-complete
239
+ ccqa hub deploy record --profile staging --sha "$GITHUB_SHA" --select
109
240
  ```
110
241
 
111
- A `report.json` (+ step screenshots) is always written to `ccqa-report/`.
112
- See [Running specs](./docs/running.md) for flags, CI recipes, and the
113
- report format.
114
-
115
- **4. Optional: share results on a hub** — `ccqa serve` starts a small
116
- self-hosted server (or use the bundled `docker-compose.yaml`). Pushing
117
- reports to it gives your team:
118
-
119
- - a dashboard of runs, with per-step screenshots
120
- - a browsable inventory of what is tested
121
- ([perspectives](./docs/spec.md#inventory-coverage-with-perspectives)),
122
- kept fresh automatically by `record`/`generate`
123
- - triage grading — mark each failure call right or wrong; the hub learns
124
- from the grades
125
- - one place for shared sessions, variables, and learned prompts — CI
126
- needs a single secret
242
+ Then, in a job of its own, run what that deploy invalidated:
127
243
 
128
244
  ```bash
129
- export CCQA_HUB_TOKEN=$(openssl rand -hex 24)
130
- ccqa serve # or: docker compose up -d
131
- ccqa run tasks/create-and-complete --push-report \
132
- --hub-url http://localhost:8787 --hub-token $CCQA_HUB_TOKEN
245
+ ccqa run --changed=last-run --profile staging --push-report
133
246
  ```
134
247
 
135
- See [Hub](./docs/hub.md) for the full setup (encryption, container
136
- deployment, HTTP API).
248
+ - `--select` records which specs the deployed range reaches. Without it, every
249
+ spec behind that entry answers `unknown` instead of `notNeeded`.
250
+ - `--changed=last-run` asks the hub, per spec, whether any deploy has touched
251
+ it since that spec last ran.
252
+
253
+ The hub has no checkout and never runs `git`, so it cannot work out what a
254
+ deploy changed. That is why the selection is submitted with the deploy rather
255
+ than reconstructed later — and why a deploy recorded without `--select` leaves
256
+ a hole nothing can fill in afterwards.
257
+
258
+ **Expect it to select nothing at first.** A spec with no recorded run is
259
+ `neverRun`; one whose baseline predates the deploy log is `unknown`. Neither
260
+ runs by default. Record a deploy, run every spec once with `--push-report`,
261
+ and the selection means something from the next deploy on. This job also reads
262
+ the spec inventory from the hub, so `ccqa perspectives` has to have run.
263
+ `--include-unknown` opts the undecided specs in.
264
+
265
+ ### On a schedule
266
+
267
+ Audit every spec against the codebase, with no browser and no deploy.
268
+
269
+ ```bash
270
+ ccqa drift --format github --push
271
+ ```
272
+
273
+ - `--severity warn|error` (default `error`) decides whether a verdict fails
274
+ the job.
275
+ - `--push` records each verdict in the hub's per-spec drift ledger, shown in
276
+ the Perspectives tab. It never changes the exit code.
277
+ - `--changed --base <ref>` narrows the sweep on a `push` workflow, at the cost
278
+ of one more model call.
279
+
280
+ The pre-merge job already audits the specs that failed. This one covers the
281
+ rest, because a spec can pass and still describe a product that no longer
282
+ exists.
283
+
284
+ ### Workflows
285
+
286
+ [CI integration](./docs/running.md#ci-integration) has runnable workflows for
287
+ the pre-merge run and the scheduled audit.
288
+ [`ccqa hub deploy record`](./docs/hub.md#ccqa-hub-deploy-record) covers the
289
+ deploy job, including a `curl`-only variant for pipelines with no Node.
137
290
 
138
291
  ## Documentation
139
292
 
140
293
  | I want to… | Read |
141
294
  |---|---|
295
+ | Look up a command or an environment variable | [Command reference](./docs/commands.md) |
142
296
  | Write specs: fields, reusable blocks, file uploads, coverage inventory | [spec.yaml reference](./docs/spec.md) |
143
297
  | Draft specs interactively with Claude | [Draft](./docs/draft.md) |
144
298
  | Generate Playwright or runn tests that reuse my existing test code | [Generation targets](./docs/targets.md) |
145
- | Run specs, read reports, triage failures, detect drift, wire up CI | [Running specs](./docs/running.md) |
299
+ | Run specs and read the report | [Running specs](./docs/running.md) |
300
+ | Classify failures and grade the calls | [Failure triage](./docs/running.md#failure-triage) |
301
+ | Audit specs against the codebase without running them | [Drift detection](./docs/running.md#drift-detection) |
302
+ | Replay only the specs a change reaches | [Scoping with `--changed`](./docs/running.md#scoping-with---changed) |
303
+ | Wire ccqa into GitHub Actions | [CI integration](./docs/running.md#ci-integration) |
146
304
  | Run specs live (no codegen), with per-project guidance | [Live specs](./docs/live.md) |
147
305
  | Start runs already signed in / skip device-trust gates | [Saved sessions](./docs/sessions.md) |
148
306
  | See which assertions generated tests use | [Assertions](./docs/assertions.md) |