ccqa 0.10.1 → 0.11.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 +45 -149
- package/dist/bin/ccqa.mjs +6232 -1867
- package/dist/hub-client/index.d.mts +246 -0
- package/dist/hub-client/index.mjs +212 -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
|
|
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,110 +168,29 @@ 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
|
|
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.
|
|
197
172
|
|
|
198
|
-
|
|
199
|
-
ccqa run tasks
|
|
173
|
+
See [Live specs](./docs/live.md) for usage examples and per-project guidance.
|
|
200
174
|
|
|
201
|
-
|
|
202
|
-
ccqa run --report
|
|
175
|
+
### Saved sessions (`session:`)
|
|
203
176
|
|
|
204
|
-
|
|
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.
|
|
209
|
-
|
|
210
|
-
### Pre-authenticated state (`statePath:`)
|
|
211
|
-
|
|
212
|
-
By default each `ccqa run` of a live spec spins up a fresh `agent-browser` session and starts signed-out. That keeps runs hermetic but forces every device-trust gate (Slack "we don't recognize this browser", Google's unfamiliar-device prompt, MFA challenges, …) to fire on every run.
|
|
213
|
-
|
|
214
|
-
To skip them, save an authenticated browser state to a JSON file once locally and point the spec at 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
|
-
title:
|
|
180
|
+
title: Admin can open the settings page
|
|
218
181
|
mode: live
|
|
219
|
-
|
|
182
|
+
session: admin # restore the saved "admin" session before step 1
|
|
220
183
|
steps:
|
|
221
|
-
- ...
|
|
184
|
+
- ... # no login steps — the spec starts signed-in
|
|
222
185
|
```
|
|
223
186
|
|
|
224
|
-
ccqa
|
|
225
|
-
|
|
226
|
-
Bootstrap once locally:
|
|
227
|
-
|
|
228
|
-
```bash
|
|
229
|
-
# 1. Log in interactively in a headed browser.
|
|
230
|
-
agent-browser --headed open https://app.slack.com
|
|
231
|
-
# …complete login + device-trust prompts by hand…
|
|
232
|
-
|
|
233
|
-
# 2. Snapshot cookies + localStorage to the path the spec references.
|
|
234
|
-
mkdir -p .ccqa/sessions
|
|
235
|
-
agent-browser state save .ccqa/sessions/slack-stg.json
|
|
236
|
-
agent-browser close
|
|
237
|
-
|
|
238
|
-
# 3. ccqa run reuses the saved state — no login prompt.
|
|
239
|
-
ccqa run slack/app-home-non-admin-access-denied
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
Add `.ccqa/sessions/` to `.gitignore` — these files contain live auth cookies and must never be committed.
|
|
243
|
-
|
|
244
|
-
#### CI: bring the state file with you
|
|
245
|
-
|
|
246
|
-
`statePath:` lives entirely inside `.ccqa/` and never touches `~/`. CI re-uses the state by writing the file into the same path the spec already references:
|
|
247
|
-
|
|
248
|
-
```bash
|
|
249
|
-
# Locally, after the interactive bootstrap above:
|
|
250
|
-
base64 -i .ccqa/sessions/slack-stg.json | pbcopy
|
|
251
|
-
# paste into your CI secret store as CCQA_SLACK_STG_STATE_B64
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
```yaml
|
|
255
|
-
# .github/workflows/ccqa.yml (sketch)
|
|
256
|
-
- name: Restore agent-browser state
|
|
257
|
-
env:
|
|
258
|
-
CCQA_SLACK_STG_STATE_B64: ${{ secrets.CCQA_SLACK_STG_STATE_B64 }}
|
|
259
|
-
run: |
|
|
260
|
-
mkdir -p .ccqa/sessions
|
|
261
|
-
printf '%s' "$CCQA_SLACK_STG_STATE_B64" | base64 -d \
|
|
262
|
-
> .ccqa/sessions/slack-stg.json
|
|
263
|
-
|
|
264
|
-
- name: Run live specs
|
|
265
|
-
env:
|
|
266
|
-
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
267
|
-
run: pnpm ccqa run --report
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
Caveats:
|
|
271
|
-
|
|
272
|
-
- **Expiry.** Whatever the upstream service's "remember this device" window is (Slack ≈ 30 days, others vary), the cookies in the state file eventually expire and CI starts failing on the device-trust gate again. Re-bootstrap locally and rotate the secret.
|
|
273
|
-
- **Treat the file as a credential.** It contains live auth cookies. Store it in your CI secret manager (GitHub Actions encrypted secrets, Vault, …) and never commit it.
|
|
274
|
-
- **Deterministic specs ignore `statePath:`.** Today it only affects `mode: live`; vitest-replayed specs always run isolated.
|
|
275
|
-
|
|
276
|
-
### Per-project guidance (`.ccqa/prompts/live.user.md` + `live.agent.md`)
|
|
277
|
-
|
|
278
|
-
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):
|
|
279
|
-
|
|
280
|
-
- `.ccqa/prompts/live.user.md` — human-maintained stable guidance.
|
|
281
|
-
- `.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`.
|
|
282
|
-
|
|
283
|
-
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`.
|
|
284
|
-
|
|
285
|
-
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:
|
|
286
|
-
|
|
287
|
-
- "the rich text editor is `[contenteditable='true']` — use `fill`, not keystrokes"
|
|
288
|
-
- "login redirects through an IDP service-selection screen; you can skip it by opening the destination URL directly"
|
|
289
|
-
- "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.
|
|
290
188
|
|
|
291
|
-
|
|
189
|
+
See [Saved sessions](./docs/sessions.md) for how to bootstrap a session and use it in CI.
|
|
292
190
|
|
|
293
|
-
|
|
294
|
-
- restating the STEP_RESULT contract (already in the system prompt)
|
|
295
|
-
- copy-pasted style guidelines from `record.user.md` (the relaxed-constraint mode doesn't need them)
|
|
191
|
+
## CI result aggregation (hub)
|
|
296
192
|
|
|
297
|
-
|
|
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.
|
|
298
194
|
|
|
299
195
|
## License
|
|
300
196
|
|