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 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`. Claude drives the browser, ccqa compiles every action into a `test.spec.ts` you can 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` directly, judges pass/fail against the step's `expected`, and saves a before/after screenshot. More flexible for fragile UIs.
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 pointing `statePath:` at a saved agent-browser state file (cookies + localStorage). Run an interactive login locally once, save the state with `agent-browser state save .ccqa/sessions/<name>.json`, then commit the path (not the file) — see [Pre-authenticated state](#pre-authenticated-state-statepath) below for the local bootstrap and the CI restore pattern.
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 you can opt in to an HTML run report by passing `--report` every failing spec gets a drift audit plus a root-cause call (TEST_DRIFT / SPEC_CHANGE / PRODUCT_BUG) using the branch's git diff as context, and the report lets a human grade those calls to measure their accuracy. Requires `ANTHROPIC_API_KEY` or a local Claude login for the analysis part. Opt out with `--no-failure-analysis` (which also implicitly skips the drift audit — the audit is rendered as evidence under the classification, so without the classification the cost has nowhere to land). Use `--no-drift-audit` to keep the classification but skip the audit. See [Run report](./docs/report.md).
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
- All Claude-driven commands accept `-m, --model <name>` (alias `sonnet` | `opus` | `haiku`, or a full model ID). The flag overrides `CCQA_MODEL`; when both are unset, the Claude Code CLI default is used. They also accept `--language <bcp47>` (e.g. `ja`, `en`) to set the language of human-readable output; the default `auto` follows the language of the spec/codebase. `--cwd <path>` works on `record` / `run` / `drift` so you can target a subpackage inside a monorepo from the repo root. Interactive commands authenticate via your local Claude Code login; commands that talk to Claude in CI (`ccqa run --report`, `ccqa drift`) additionally honor `ANTHROPIC_API_KEY`.
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 the on-disk path `.ccqa/features/<feature>/test-cases/<spec>/`. `ccqa run` accepts several targets space-separated (each a `<feature>/<spec>`, a bare `<feature>` for all its specs, or omitted for everything); duplicates are de-duped and `--changed` cannot be combined with explicit targets.
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 (machine-readable, canonical)
134
- perspectives.md # Category index, regenerated from the YAML
135
- profiles/ # `--profile <name>` env files
136
- stg.env # URLs + credential refs; commit if it uses secret-manager refs, gitignore if it holds plaintext secrets
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 only) Recorded actions from `ccqa record`
153
- test.spec.ts # (deterministic only) Generated vitest script
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
- Add `.ccqa/features/*/test-cases/*/runs/` to `.gitignore` — these are per-run artefacts that should not be committed. Likewise `ccqa-report*/`.
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
- - Name is free-form (`stg`/`prd` are conventions); a path separator / `..` / leading dot is rejected, and a missing profile exits 2. Only the name is logged, never values.
181
- - Format is a small `.env` subset (`KEY=value`, `#` comments, `export`, quotes). Profile values **override** the inherited environment.
182
- - Without `--profile`, ccqa auto-loads `<cwd>/.env` if present (like dotenv); with neither, `${VAR}` resolves against the existing `process.env` (e.g. `direnv`).
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
- ```bash
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
- # Run every spec under a feature (mixes deterministic + live as declared)
199
- ccqa run tasks
173
+ See [Live specs](./docs/live.md) for usage examples and per-project guidance.
200
174
 
201
- # Run every spec in the project, into a unified HTML report
202
- ccqa run --report
175
+ ### Saved sessions (`session:`)
203
176
 
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.
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: Slack App Home non-admin access denied
180
+ title: Admin can open the settings page
218
181
  mode: live
219
- statePath: .ccqa/sessions/slack-stg.json # cookies + localStorage to restore
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 resolves the path against the project root and passes `--state <path>` to every `agent-browser` invocation in the run (including ccqa's own screenshot calls). The file is **read-only** `--state` loads it but never writes back to it. Re-running locally or in CI does not mutate it.
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
- Examples of what does **not** belong:
189
+ See [Saved sessions](./docs/sessions.md) for how to bootstrap a session and use it in CI.
292
190
 
293
- - per-spec details (those belong in the spec's `instruction` / `expected`)
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
- The combined bundle is capped at 32 KiB; anything beyond that is truncated with a warning.
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