ccqa 0.10.2 → 0.12.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 +41 -141
- package/dist/bin/ccqa.mjs +6389 -1990
- package/dist/hub-client/index.d.mts +257 -0
- package/dist/hub-client/index.mjs +213 -0
- package/dist/package.json +5 -1
- package/dist/runtime/vitest.config.d.mts +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -4,10 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
ccqa turns Claude Code into a browser test recorder. Write a spec in YAML, declare in the spec whether it should run **deterministic** or **live**, then `ccqa run` does the right thing per spec:
|
|
6
6
|
|
|
7
|
-
- **Deterministic** (`mode: deterministic`, default): record once with `ccqa record
|
|
8
|
-
- **Live** (`mode: live`): no codegen. `ccqa run` sends each step to Claude every time
|
|
9
|
-
|
|
10
|
-
A single project mixes both: each spec.yaml picks its own mode, and `ccqa run` reads the field and dispatches. The HTML report covers both in one page.
|
|
7
|
+
- **Deterministic** (`mode: deterministic`, default): record once with `ccqa record` — Claude drives the browser and ccqa compiles every action into a `test.spec.ts` you replay in CI under vitest, no LLM at run time. Cheapest and most stable.
|
|
8
|
+
- **Live** (`mode: live`): no codegen. `ccqa run` sends each step to Claude every time; Claude drives `agent-browser`, judges pass/fail against the step's `expected`, and saves a before/after screenshot. More flexible for fragile UIs.
|
|
11
9
|
|
|
12
10
|
No extra API key. Just `claude`.
|
|
13
11
|
|
|
@@ -15,6 +13,8 @@ No extra API key. Just `claude`.
|
|
|
15
13
|
|
|
16
14
|
## How it works
|
|
17
15
|
|
|
16
|
+
Each spec picks its own mode; `ccqa run` reads the field and dispatches. One project mixes both, and one report covers both.
|
|
17
|
+
|
|
18
18
|
```mermaid
|
|
19
19
|
flowchart LR
|
|
20
20
|
A["Write spec\n(spec.yaml + mode:)"] --> B{mode}
|
|
@@ -23,10 +23,6 @@ flowchart LR
|
|
|
23
23
|
B -- live --> E["ccqa run\n(Claude drives every time,\nper-step pass/fail)"]
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
For deterministic specs, `record` invokes Claude Code with your spec, Claude drives the browser step by step, every action is recorded, and a vitest-compatible script is generated. `run` then replays it without involving an LLM.
|
|
27
|
-
|
|
28
|
-
For live specs, `record` is not needed. `run` directly sends each step to Claude, which drives the browser through `agent-browser`, judges whether the step's `expected` clause holds, and writes a PNG before and after each step. Useful when codegen is fragile (timing-dependent UIs, rich-text editors, dynamic selectors).
|
|
29
|
-
|
|
30
26
|
## Install
|
|
31
27
|
|
|
32
28
|
```bash
|
|
@@ -69,11 +65,9 @@ ccqa run tasks/create-and-complete # vitest replays test.spec.ts; no LLM
|
|
|
69
65
|
ccqa run tasks/create-and-complete # Claude drives the browser every time
|
|
70
66
|
```
|
|
71
67
|
|
|
72
|
-
Live specs can start already-signed-in by naming a saved session with `session
|
|
73
|
-
|
|
74
|
-
By default deterministic runs write step-boundary screenshots and metadata to `ccqa-report/evidence/<feature>/<spec>/` so a reviewer can confirm a passing spec actually reached the states its `expected` clauses describe. Disable with `--no-evidence`.
|
|
68
|
+
Live specs can start already-signed-in by naming a saved session with `session:` — see [Saved sessions](./docs/sessions.md). Deterministic runs also write step-boundary screenshots to `ccqa-report/evidence/` (disable with `--no-evidence`).
|
|
75
69
|
|
|
76
|
-
In CI
|
|
70
|
+
In CI, add `--report` to write a `report.json` (+ evidence PNGs) you push to the [ccqa hub](./docs/hub.md) and view in its UI; each failing spec gets a root-cause call (TEST_DRIFT / SPEC_CHANGE / PRODUCT_BUG) you can grade for accuracy. Needs `ANTHROPIC_API_KEY` or a local Claude login. See [Run report](./docs/report.md).
|
|
77
71
|
|
|
78
72
|
```bash
|
|
79
73
|
ccqa run tasks/create-and-complete --report --base origin/main
|
|
@@ -85,6 +79,8 @@ ccqa run --changed --report # only specs whose relatedPaths t
|
|
|
85
79
|
| Feature | Docs |
|
|
86
80
|
|---|---|
|
|
87
81
|
| Write specs interactively with Claude | [Draft](./docs/draft.md) |
|
|
82
|
+
| Run specs live (no codegen), with per-project guidance | [Live specs](./docs/live.md) |
|
|
83
|
+
| Restore a saved login to skip device-trust gates | [Saved sessions](./docs/sessions.md) |
|
|
88
84
|
| Reuse login and other shared step sequences | [Blocks](./docs/blocks.md) |
|
|
89
85
|
| Drive `<input type="file">` without an OS picker | [File upload](./docs/file-upload.md) |
|
|
90
86
|
| Assertion helper functions | [Assertions](./docs/assertions.md) |
|
|
@@ -93,6 +89,7 @@ ccqa run --changed --report # only specs whose relatedPaths t
|
|
|
93
89
|
| HTML run report with failure root-cause calls | [Run report](./docs/report.md) |
|
|
94
90
|
| Inventory existing test coverage | [Perspectives](./docs/perspectives.md) |
|
|
95
91
|
| Architecture decision records (why it is built this way) | [ADR](./docs/adr/README.md) |
|
|
92
|
+
| Aggregate CI run results, sessions, and variables on a shared server | [Hub](./docs/hub.md) |
|
|
96
93
|
|
|
97
94
|
## Commands
|
|
98
95
|
|
|
@@ -105,63 +102,44 @@ ccqa run [feature/spec...] Execute specs. Per spec, the spec.yaml `mode:
|
|
|
105
102
|
(vitest replay) or live (Claude drives every time). One run can mix both;
|
|
106
103
|
`--report` writes one unified HTML. Pass multiple targets space-separated.
|
|
107
104
|
ccqa drift [feature/spec] Standalone spec ↔ codebase static audit (for PR checks)
|
|
105
|
+
ccqa serve Start a hub: a control-plane HTTP server that aggregates run results,
|
|
106
|
+
sessions, and variables (it does not execute tests)
|
|
107
|
+
ccqa hub push Push a finished run's report to a hub
|
|
108
|
+
ccqa hub session|var <cmd> Manage sessions/variables stored on a hub
|
|
108
109
|
```
|
|
109
110
|
|
|
110
|
-
`ccqa run` flags:
|
|
111
|
-
|
|
112
|
-
- `--report [dir]` — write a self-contained HTML run report (default dir: `ccqa-report/`)
|
|
113
|
-
- `--profile <name>` — load `.ccqa/profiles/<name>.env` into the environment before resolving spec `${VAR}` references, so one spec targets dev/stg/prd without per-environment copies. See [Profiles](#profiles---profile).
|
|
114
|
-
- `--changed` — restrict execution to specs whose `relatedPaths` intersect `git diff <base>...HEAD`. Mutually exclusive with explicit spec targets.
|
|
115
|
-
- `--concurrency <n>` — run up to N specs in parallel **within each mode** (deterministic specs run as one phase, live specs as the next; parallelism is within a phase, not across). Default `1` (sequential, identical to before). Above 1, each spec's output is buffered and flushed as a labelled block so parallel logs stay legible. Live specs each launch their own headed Chrome, so high values spawn many browser instances.
|
|
116
|
-
- `--base <ref>` — base ref for the git diff (default: `$GITHUB_BASE_REF`, then `origin/main`)
|
|
117
|
-
- `--no-failure-analysis` — skip the per-failure root-cause classification (also skips the drift audit, since the audit only shows under the classification)
|
|
118
|
-
- `--no-drift-audit` — skip the spec ↔ code drift audit while keeping the classification
|
|
119
|
-
- `--no-evidence` — (deterministic specs only) skip step-boundary PNG capture
|
|
120
|
-
- `--retry <n>` — (live specs only) retry each failing step up to N more times
|
|
121
|
-
- `--format <fmt>` — `text` (default), `json` (report.json), `github` (Actions annotations)
|
|
122
|
-
- `--out <dir>` — (live specs only, single-spec invocations) override the per-run artifact directory
|
|
123
|
-
- `--update-agent-prompt` — (live specs only) after the run, summarise it back to Claude and rewrite `.ccqa/prompts/live.agent.md` so the next run inherits the lessons learned. `ccqa record` ships the same flag, refreshing `record.agent.md` from the trace summary.
|
|
111
|
+
Key `ccqa run` flags (see `ccqa run --help` for the rest):
|
|
124
112
|
|
|
125
|
-
|
|
113
|
+
- `--report [dir]` — write the run report (report.json + evidence PNGs) for `ccqa hub push` (default dir: `ccqa-report/`)
|
|
114
|
+
- `--profile <name>` — load `.ccqa/profiles/<name>.env` before resolving spec `${VAR}` references, so one spec targets dev/stg/prd. See [Profiles](#profiles---profile).
|
|
115
|
+
- `--changed` — restrict execution to specs whose `relatedPaths` intersect `git diff <base>...HEAD`
|
|
116
|
+
- `--concurrency <n>` — run up to N specs in parallel **within each mode** (deterministic phase, then live phase — parallelism is per-phase, not across phases). Default `1` (sequential, same behavior as before).
|
|
117
|
+
- `--no-failure-analysis` / `--no-drift-audit` — `--no-failure-analysis` skips failure classification (and its drift audit, since drift is the classification's evidence); `--no-drift-audit` keeps classification but skips just the audit.
|
|
118
|
+
- `--format <fmt>` — `text` (default), `json` (report.json), `github` (Actions annotations)
|
|
126
119
|
|
|
127
|
-
`<feature/spec>` is a 2-segment alias for
|
|
120
|
+
`<feature/spec>` is a 2-segment alias for `.ccqa/features/<feature>/test-cases/<spec>/`; `ccqa run` takes several space-separated targets (a `<feature>/<spec>`, a bare `<feature>`, or none for everything). Claude-driven commands accept `-m/--model`, `--language`, and `--cwd` (for monorepos); they use your local Claude login, and CI commands (`ccqa run --report`, `ccqa drift`) also honor `ANTHROPIC_API_KEY`.
|
|
128
121
|
|
|
129
122
|
## File structure
|
|
130
123
|
|
|
131
124
|
```
|
|
132
125
|
.ccqa/
|
|
133
|
-
perspectives.yaml # Inventory of existing coverage (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
prd.env
|
|
138
|
-
prompts/ # Run `ccqa init` to scaffold these
|
|
139
|
-
record.user.md # Human-maintained guidance appended to `ccqa record` (trace phase)
|
|
140
|
-
record.agent.md # Auto-updated by `ccqa record --update-agent-prompt`
|
|
141
|
-
live.user.md # Human-maintained guidance appended to `ccqa run` (live specs)
|
|
142
|
-
live.agent.md # Auto-updated by `ccqa run --update-agent-prompt`
|
|
126
|
+
perspectives.yaml # Inventory of existing coverage (canonical)
|
|
127
|
+
profiles/ # `--profile <name>` env files (stg.env, prd.env, ...)
|
|
128
|
+
prompts/ # `ccqa init` scaffolds these; *.user.md human-maintained, *.agent.md auto-updated
|
|
129
|
+
record.user.md record.agent.md live.user.md live.agent.md
|
|
143
130
|
blocks/
|
|
144
|
-
login/
|
|
145
|
-
spec.yaml # Reusable block (params + steps)
|
|
131
|
+
login/spec.yaml # Reusable block (params + steps)
|
|
146
132
|
features/
|
|
147
133
|
tasks/
|
|
148
|
-
perspectives.md # Per-category detail tables (one per case)
|
|
149
134
|
test-cases/
|
|
150
135
|
create-and-complete/
|
|
151
136
|
spec.yaml # Test definition, with `mode: deterministic | live`
|
|
152
|
-
actions.json # (deterministic
|
|
153
|
-
test.spec.ts # (deterministic
|
|
154
|
-
runs/
|
|
155
|
-
2026-06-14T10-00-00-000Z/ # (live only) one `ccqa run` invocation
|
|
156
|
-
run.json # Machine-readable summary
|
|
157
|
-
run.md # Human-readable per-step log
|
|
158
|
-
steps/
|
|
159
|
-
step-01.before.png # Before-step screenshot
|
|
160
|
-
step-01.after.png # After-step screenshot
|
|
161
|
-
step-01.log.txt # Claude's full transcript for the step
|
|
137
|
+
actions.json # (deterministic) recorded actions
|
|
138
|
+
test.spec.ts # (deterministic) generated vitest script
|
|
139
|
+
runs/<timestamp>/ # (live) one `ccqa run` — run.json/run.md + steps/*.png
|
|
162
140
|
```
|
|
163
141
|
|
|
164
|
-
|
|
142
|
+
Gitignore the per-run artefacts: `.ccqa/features/*/test-cases/*/runs/` and `ccqa-report*/`.
|
|
165
143
|
|
|
166
144
|
## Profiles (`--profile`)
|
|
167
145
|
|
|
@@ -177,11 +155,10 @@ TEST_USER_PASSWORD=...
|
|
|
177
155
|
ccqa run auth/login --profile stg # same spec, stg values
|
|
178
156
|
```
|
|
179
157
|
|
|
180
|
-
-
|
|
181
|
-
-
|
|
182
|
-
- Without `--profile`, ccqa auto-loads `<cwd>/.env` if present
|
|
183
|
-
|
|
184
|
-
**Secrets:** gitignore any profile that holds plaintext secrets. ccqa only parses `.env` files — it doesn't resolve secret-manager references — so to keep secrets off disk, drop `--profile` and run ccqa under your secret manager instead (e.g. `op run --env-file=.ccqa/profiles/stg.env -- ccqa run ...`), which injects the resolved values into `process.env` for ccqa to read.
|
|
158
|
+
- Format is a small `.env` subset (`KEY=value`, `#` comments, `export`, quotes); profile values **override** the inherited environment.
|
|
159
|
+
- Name is free-form (`stg`/`prd` by convention); path separators, `..`, and leading dots are rejected, and an unknown name exits 2. Only the name is logged, never values.
|
|
160
|
+
- Without `--profile`, ccqa auto-loads `<cwd>/.env` if present; with neither, `${VAR}` resolves against the existing `process.env`.
|
|
161
|
+
- **Secrets:** gitignore any profile holding plaintext secrets. To keep secrets off disk entirely, drop `--profile` and run ccqa under your secret manager (e.g. `op run --env-file=... -- ccqa run ...`), which injects resolved values into `process.env`.
|
|
185
162
|
|
|
186
163
|
## Live specs (`mode: live`)
|
|
187
164
|
|
|
@@ -191,27 +168,13 @@ For specs declared `mode: live` in their spec.yaml, `ccqa run` skips codegen ent
|
|
|
191
168
|
- the codegen output for a spec is fragile (heavily timing-dependent UIs, rich-text editors, dynamic selectors)
|
|
192
169
|
- you want a visual audit trail of what the page looked like at every step
|
|
193
170
|
|
|
194
|
-
|
|
195
|
-
# Run a single live spec
|
|
196
|
-
ccqa run tasks/create-and-complete
|
|
197
|
-
|
|
198
|
-
# Run every spec under a feature (mixes deterministic + live as declared)
|
|
199
|
-
ccqa run tasks
|
|
171
|
+
Constraints on selectors / `agent-browser` subcommands that apply during `ccqa record` (no `eval`, no `@ref`, no bare-tag positional `find`, no chained agent-browser calls) are relaxed for live specs, since there is no replay contract to honour.
|
|
200
172
|
|
|
201
|
-
|
|
202
|
-
ccqa run --report
|
|
203
|
-
|
|
204
|
-
# Retry each failing step up to 2 more times (live specs only)
|
|
205
|
-
ccqa run --retry 2 tasks/create-and-complete
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
Constraints on selectors / `agent-browser` subcommands that apply during `ccqa record` (no `eval`, no `@ref`, no bare-tag positional `find`, no chained agent-browser calls) are **relaxed** for live specs — Claude can use any subcommand and any selector style because there is no replay contract to honour.
|
|
173
|
+
See [Live specs](./docs/live.md) for usage examples and per-project guidance.
|
|
209
174
|
|
|
210
175
|
### Saved sessions (`session:`)
|
|
211
176
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
For those, save the signed-in browser state once and let the spec **restore** it. ccqa does not manage authentication — `session` is purely an optional restore of cookies + localStorage. Specs that can just log in normally don't use it.
|
|
177
|
+
Some providers gate every fresh browser with a device-trust check (an "unrecognized device" e-mail code, an MFA prompt) that a human has to clear by hand — impractical to repeat on every run, impossible in CI. `session:` restores a saved, signed-in browser state instead.
|
|
215
178
|
|
|
216
179
|
```yaml
|
|
217
180
|
title: Admin can open the settings page
|
|
@@ -221,76 +184,13 @@ steps:
|
|
|
221
184
|
- ... # no login steps — the spec starts signed-in
|
|
222
185
|
```
|
|
223
186
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
```yaml
|
|
227
|
-
session:
|
|
228
|
-
- admin # one provider, signed in as admin
|
|
229
|
-
- admin-chat # another provider, same person
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
#### Create a session — `ccqa session bootstrap`
|
|
233
|
-
|
|
234
|
-
```bash
|
|
235
|
-
# Opens a headed browser. Log in by hand (clear any device-trust gate),
|
|
236
|
-
# then press Enter and ccqa saves the session.
|
|
237
|
-
ccqa session bootstrap admin --url https://app.example.com/login
|
|
238
|
-
|
|
239
|
-
# List saved sessions (names + save times; no secret values shown).
|
|
240
|
-
ccqa session ls
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
Sessions are saved to `.ccqa/sessions/<profile>/<name>.json`. The `<profile>` is the same `--profile` that selects the `.ccqa/profiles/<profile>.env` file, so one flag picks both the environment and its sessions bucket; with no `--profile` the bucket is `default`. `ccqa init` drops a self-ignoring `.ccqa/sessions/.gitignore` so saved sessions stay out of git — **they contain live auth cookies and must never be committed.**
|
|
244
|
-
|
|
245
|
-
When a spec names a session that hasn't been created yet, the run stops and tells you which `ccqa session bootstrap` to run, rather than starting unauthenticated.
|
|
246
|
-
|
|
247
|
-
#### CI: restore the session
|
|
248
|
-
|
|
249
|
-
Saved sessions live entirely inside `.ccqa/` and never touch `~/`. In CI, write each session file back to the path the run expects (read it from your secret store):
|
|
250
|
-
|
|
251
|
-
```bash
|
|
252
|
-
# Locally, after bootstrapping, copy the file into your CI secret store:
|
|
253
|
-
base64 -i .ccqa/sessions/default/admin.json | pbcopy # paste as CCQA_SESSION_ADMIN_B64
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
```yaml
|
|
257
|
-
# CI step (sketch) — restore before `ccqa run`
|
|
258
|
-
- name: Restore session
|
|
259
|
-
env:
|
|
260
|
-
CCQA_SESSION_ADMIN_B64: ${{ secrets.CCQA_SESSION_ADMIN_B64 }}
|
|
261
|
-
run: |
|
|
262
|
-
mkdir -p .ccqa/sessions/default
|
|
263
|
-
printf '%s' "$CCQA_SESSION_ADMIN_B64" | base64 -d > .ccqa/sessions/default/admin.json
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
Notes:
|
|
267
|
-
|
|
268
|
-
- **Expiry.** The provider's "remember this device" window eventually lapses and the saved cookies stop working. Re-run `ccqa session bootstrap` locally and rotate the secret.
|
|
269
|
-
- **Treat session files as credentials.** They hold live auth cookies. Keep them in a secret manager; never commit them.
|
|
270
|
-
- **Deterministic specs ignore `session:`.** It only affects `mode: live`; vitest-replayed specs always run isolated.
|
|
271
|
-
|
|
272
|
-
### Per-project guidance (`.ccqa/prompts/live.user.md` + `live.agent.md`)
|
|
273
|
-
|
|
274
|
-
ccqa's live-mode system prompt is deliberately product-agnostic. Anything specific to **your** project — staging URLs, login flow quirks, rich-editor types, common access-denied wording — belongs in two sibling files (run `ccqa init` to scaffold both):
|
|
275
|
-
|
|
276
|
-
- `.ccqa/prompts/live.user.md` — human-maintained stable guidance.
|
|
277
|
-
- `.ccqa/prompts/live.agent.md` — auto-updated by `ccqa run --update-agent-prompt` from each run's summary. You can hand-edit it, but the next `--update-agent-prompt` run may rewrite the whole file; durable rules should live in `live.user.md`.
|
|
278
|
-
|
|
279
|
-
Both files (when present) are read once per invocation and appended to the system prompt under "Project-specific guidance". The `ccqa record` (trace) side has the same split: `record.user.md` + `record.agent.md`, refreshed by `ccqa record --update-agent-prompt`.
|
|
280
|
-
|
|
281
|
-
Keep them short. A page or two of focused notes beats a long handbook — Claude has the spec's `expected` text to work from, these files are for the *non-obvious* product knowledge that isn't in any single spec. Examples of what's useful here:
|
|
282
|
-
|
|
283
|
-
- "the rich text editor is `[contenteditable='true']` — use `fill`, not keystrokes"
|
|
284
|
-
- "login redirects through an IDP service-selection screen; you can skip it by opening the destination URL directly"
|
|
285
|
-
- "access-denied is signalled by a specific in-app message string — name it here so the model asserts on it"
|
|
187
|
+
ccqa does not manage authentication — `session` is purely an optional restore of cookies + localStorage.
|
|
286
188
|
|
|
287
|
-
|
|
189
|
+
See [Saved sessions](./docs/sessions.md) for how to bootstrap a session and use it in CI.
|
|
288
190
|
|
|
289
|
-
|
|
290
|
-
- restating the STEP_RESULT contract (already in the system prompt)
|
|
291
|
-
- copy-pasted style guidelines from `record.user.md` (the relaxed-constraint mode doesn't need them)
|
|
191
|
+
## CI result aggregation (hub)
|
|
292
192
|
|
|
293
|
-
|
|
193
|
+
`ccqa serve` starts a hub — a control-plane HTTP server that aggregates CI run results, saved sessions, and variables on a shared server. It does not execute tests: a CI job (or a laptop) runs `ccqa run --report` as usual, fetching whatever sessions/variables/prompts it needs from the hub directly at run time, then `ccqa hub push` uploads the resulting report to the hub. Sessions and variables are registered on the hub ahead of time (`ccqa hub session push`, `ccqa hub var set`, or the bundled UI's Secrets tab), so a CI job needs only one secret: `CCQA_HUB_TOKEN`. One hub manages many projects — secrets are scoped per project/profile, and `--project` defaults to the current directory's name. See [docs/hub.md](./docs/hub.md) for setup and usage.
|
|
294
194
|
|
|
295
195
|
## License
|
|
296
196
|
|