# Changelog

## [2.2.1] - 2026-09-14

### Fixed

- **`qualiow session finalize` writes its rows in the layout the index file already has.** A project initialised before the `Kind` column existed has an `output/sessions/INDEX.md` whose header is `| Date | Target | Bugs | Duration | Status | Report |`. Finalize built every row from the current seven columns and appended it regardless, so the row was one cell wider than its own header and reading it back by column name shifted each value one column to the right — the report link fell off the end, and because the already-indexed check looks for the session directory in the report cell, `qualiow list sessions` then showed the session twice, once as a scrambled row and once as unindexed. The header is now read first: a canonical header behaves exactly as before, and a header that differs gets the row in its own column order, mapped by column name with unrepresented columns left empty. Existing rows are never widened or rewritten, and the older layout is reported on the command line rather than silently accommodated. The same rule covers `output/bugs/all-bugs.md`. `session archive --remove` locates the Status cell from the file's own header instead of assuming its position, and the `delete` / `prune` row counts are matched by reference rather than by column name, so both hold on either layout.
- **A session index split into blocks by blank lines no longer swallows rows.** Blank lines between the rows of `output/sessions/INDEX.md` — easy to leave behind when the file has been maintained by hand — split one markdown table into several one-row blocks, and a reader stops at the first blank line: the rows below it are on disk and invisible, so `list sessions` undercounts and a session that was just finalized reads back as unindexed. `finalize` now writes its row directly after the last row of the table instead of at end of file, which on a file whose table runs to the end is the same bytes as before, and reports what a reader cannot see (`○ INDEX.md has 3 row(s) outside the first table block (blank lines split it); readers see 2 of 5 — run \`qualiow session repair-index\``) with the counts taken from the file rather than estimated. The already-indexed check reads every block, so a split table cannot collect a second copy of a row. New **`qualiow session repair-index [--yes]`** closes the gap: a dry run by default reporting the blank lines between rows and the rows that would become visible, and with `--yes` it removes only the blank lines sitting strictly between two rows of the same table. Row text is never reflowed, re-aligned, re-ordered or rewritten — a row carrying a stray `|` survives byte for byte — and blank lines before the table, after it and between the title and the header stay where they are, as does a second table under its own heading. It covers `output/bugs/all-bugs.md` too, and a second run reports nothing to do.
- **Session directories named before the current scheme are visible to cleanup again.** `prune` and the unindexed scan in `list sessions` discovered directories through `SESSION_DIR_RE`, which only matches `<YYYY-MM-DD-HHmm>-<kind>-<slug>`, so output an upgraded project already had on disk (`2026-05-22-1045-demo-target`, `2026-07-09-quick-preprod-product-search`) could never be selected and was not even reported as present. `LEGACY_SESSION_DIR_RE` and `describeSessionDir` in `src/utils/session-dir.ts` now cover any date-prefixed name for DISCOVERY ONLY — reading the `HHmm` when the name carries one and treating it as local midnight when it does not, so ages compare — while `sessionDirName`, `parseSessionDirName` and every creation path keep their current strictness: nothing changes about how a new directory is named. `prune` marks the legacy names in its dry run, the listing marks them `unindexed (legacy)`, and a report cell written in the `[label](path)` link form now resolves to its session directory so an indexed legacy session is no longer counted as unindexed as well.
- **`bin/qualiow` explains an unbuilt checkout instead of failing through `npx`.** Run the
  launcher with the working directory anywhere inside a copy of this repository that has no
  `dist/` and no `node_modules` — a fresh clone, or the plugin directory itself — and npm
  resolved the local project context, found a `package.json` named
  `qualiow-exploratory-testing` with no linked bin, and exited with a bare
  `sh: qualiow: command not found`. The launcher now detects that case and names the fix
  (build once, or run from a project directory outside the tree). A plugin install invoked
  from a user's project still takes the `npx` path unchanged; `tests/unit/shim.test.ts` covers
  the local-build path, the guard and a nested directory. Root resolution moved to `pwd -P` so
  the comparison holds through symlinked paths.

## [2.2.0] - 2026-09-14

The second half of the routing work 2.1.0 started. 2.1.0 gave the deterministic work to the CLI; this release gives the bounded reads and the report assembly to four cheap sub-agents, and adds two `PreToolUse` hooks that enforce the read thresholds instead of merely stating them. The judgement stays exactly where it was: severity, priority, business impact, the bug reports, the charter and risk ranking, what is missing, the AC verdicts, the executive summary and the reflection are still written by the session. The interaction phases are untouched — element refs and the observe-decide loop never leave the session. Additive: no skill, command, schema or library export was renamed or removed. Rationale, the routing procedure and the exclusions: `docs/ARCHITECTURE-DECISIONS.md` ADR-011.

### Added

- **Four sub-agents** in `.claude/agents/` (mirrored to `agents/`), each pinning its own model and turn ceiling, none of them allowed to return a verdict. `qa-gather-agent` (`sonnet`) now pins a model and gains size gates — `wc -l` before a file, `git diff --stat` before any diff, anything over 300 lines read by section. `qa-reporting-agent` (`sonnet`, `effort: low`) reads the session directory in windows and assembles `session-report.md` per `references/output-contract.md`, copying the executive summary, recommendations and reflection from `phase-7-notes.md` rather than inventing them, then runs `qualiow session finalize` and fixes only header and format violations. `qa-diff-indexer-agent` (`haiku`, `effort: low`) returns a table of file → symbols or resources → line ranges → candidate AC ids, plus the files that map to no AC and the ACs that map to no file. `qa-page-mapper-agent` (`haiku`, `effort: low`) turns one raw snapshot into forms, navigation, interactive controls, visible error and empty-state text and hidden/disabled counts — never test ideas, never bugs. None declares `permissionMode`: a plugin-distributed agent rejects the field (`docs/KNOWN-ISSUES.md` ISSUE-001).
- **`hooks/`** — `hooks.json` at the plugin's default discovery path (no `hooks` key in `plugin.json`, which would register them twice) plus `scripts/read-guard.mjs`, `scripts/write-guard.mjs` and `scripts/secret-patterns.mjs`, node builtins only so they run in a plugin install with no `dist/`. `read-guard.mjs` (matcher `Read`) fires only for `data/knowledge/manifest.yml`, `data/knowledge/releases/**`, `output/sessions/*/phase-*.md` and `output/sessions/*/snapshots/*`, and denies a whole-file read over `QUALIOW_READ_MAX_LINES` (default 300) with the cheaper route named in the refusal — `kb digest`, `list knowledge --entry <id>`, `Grep` plus a windowed `Read`, or `qa-page-mapper-agent`; a `Read` that already carries `offset` or `limit` passes. `write-guard.mjs` (matcher `Write|Edit|MultiEdit`) fires only for files under `output/` and never inside `snapshots/`, and denies content matching the redaction list, naming the categories — security rule 3 was prompt-only until now. The hook duplicates the patterns rather than calling the CLI, because a hook has to answer in milliseconds; `tests/unit/hooks.test.ts` asserts parity with `containsSecrets` on the samples from `redact.test.ts`.
- **`qualiow init --hooks`** — copies the guard scripts to `qa/hooks/` (not gitignored: a committed settings file must not point at ignored files) and merges into `.claude/settings.json` the two hook entries and `permissions.allow` rules for `Bash(playwright-cli:*)`, `Bash(npx playwright-cli:*)` and `Bash(qualiow:*)`. Merge is by exact string and idempotent, and hooks already in the file are preserved. `package.json` `files` and `scripts/check-pack.mjs` cover `hooks/`.
- **`phase-7-notes.md`** in the session directory — the session's own header-first notes: executive summary, coverage-map rows, observations, areas not tested, recommendations and reflection. It is what the reporting agent assembles the report around, and it is a valid input to the fallback path where the session writes the report itself.
- **A snapshot policy for discovery**, in place of "snapshot before every interaction" with no size rule: `snapshot --depth=3` as the default on a discovery page, `snapshot <ref>` to zoom, `find "<text>"` for a single label, and a full tree only via `playwright-cli --raw snapshot` into `output/sessions/<dir>/snapshots/<page>.yml` — read directly at 300 lines or fewer, routed to `qa-page-mapper-agent` above that. `references/session-rules.md` carries the same one-line policy.
- **`tests/unit/agents-lint.test.ts`** — the agent-file counterpart of the skills lint: allowed frontmatter keys, `model` restricted to `haiku|sonnet|opus|inherit`, `name` equal to the file name, and `permissionMode` forbidden. `tests/unit/plugin-manifest.test.ts` requires all four agents to be present and mirrored.

### Changed

- **`/qa-gather` and `/qa-explore-report` run forked.** Both declare `context: fork` and their agent (`qa-gather-agent`, `qa-reporting-agent`), so the sources a gather reads and the phase files a report re-reads never enter the session context. Gather's consequence is documented in the skill: it starts with the invocation and nothing else, so paths, URLs or pasted text must be in the same message, and gaps stay `[GAP]` / `[ASSUMPTION]` markers instead of becoming a follow-up question.
- **The reporting phases hand assembly to the agent.** `qa-explore/phases/07-reporting.md`, `qa-explore-mobile/phases/07-reporting.md` (notes carry `## Mobile Context` and `## Deferred Tests`) and `qa-verify-backend/phases/05-reporting.md` (notes carry `## AC Matrix Summary`) write the bug reports, `stats.json` and `phase-7-notes.md` themselves, then invoke `qa-reporting-agent` with the session directory and kind; unresolved violations are fixed and `finalize --check` re-run. If the Agent tool is unavailable the session writes the report itself and runs `finalize` — the route is a fallback, not a dependency. Quick sessions still write their own report: an agent spin-up costs more than it saves at that size.
- **The backend static lane has a diff gate.** `qa-verify-backend/phases/02-static-review.md` runs `git diff --stat` first and, at 25 files or 1,500 changed lines or more, routes the diff through `qa-diff-indexer-agent` with the phase-1 AC list, then reads only the ranges it names with `git show <branch>:<path>`. The probe catalogues are read by section: `03-live-verification.md` and `03b-api-verification.md` `Grep` `references/aws-readonly-probes.md` and `references/api-probes.md` for the resource heading and `Read` that section with `offset`/`limit` instead of the whole file. `/qa-explore-feedback` reads a long session report in windows and skips the phase files it does not need.
- **`references/delegation-rules.md` tier 1 is filled in** — the four agents by name (short and `qualiow:`-prefixed), what each returns, and the overrides that were pending: `QUALIOW_HOOKS=off`, `QUALIOW_READ_MAX_LINES`, `model: inherit` in a project's copy of an agent, and `CLAUDE_CODE_SUBAGENT_MODEL`. `references/security-rules.md` records that `qualiow session finalize` and the write guard enforce the same redaction list.
- **Skill frontmatter accepts `effort` and `background`** (`tests/unit/skills-lint.test.ts` `ALLOWED_FRONTMATTER_KEYS`), which is what lets a skill pin a cheaper reasoning budget for the forked work.

### Notes

- **The hooks are on by default in a plugin install** and opt-in everywhere else. They are scoped to qualiow-owned paths — the knowledge base, session phase files, session snapshots, and writes under `output/` — so they never touch ordinary coding in the same project. `QUALIOW_HOOKS=off` in `.claude/settings.json` `env` disables both; `QUALIOW_READ_MAX_LINES` moves the read ceiling. Do not enable both the plugin's hooks and a project's `init --hooks` copies: the result is a harmless double deny and two node processes per tool call.
- **`/qa-gather` needs its input in the same message.** The fork has no way to ask.
- **Release order is unchanged from 2.1.0:** `npm publish` lands before `git push --follow-tags` and the GitHub release, because `bin/qualiow` pins its `npx` fetch to the version in `.claude-plugin/plugin.json`.

## [2.1.0] - 2026-09-14

_Merged to `main` ahead of 2.2.0 and shipped inside it; there is no standalone 2.1.0 on npm. Everything in this section is present in 2.2.0._

Work with a fixed contract moves out of the session and into the `qualiow` CLI: the knowledge load becomes a digest, the end of a session becomes one `session finalize` call, and the knowledge-list and cleanup skills become wrappers over commands the CLI already needed. The repository also becomes its own Claude Code marketplace, which required a launcher shim to make the CLI reachable from a plugin install. Additive — no skill, command, schema or library export was renamed or removed. The second half of the routing work (cheap-model sub-agents for the I/O-heavy reads, and hooks that enforce the read thresholds) lands in 2.2.0.

### Added

- **`qualiow kb digest`** — `digest [--domain <id>] [--tag <t>…] [--for explore|backend|mobile] [--entry <id>] [--data <dir>] [--max-lines <n>]`. Prints the entries a session actually needs — the `always` set plus whatever the domain, the tags and the skill select — as a few lines each: id, type, priority and tags, the summary, the named sub-items with their first question, and `when_to_use`. Until now a session read `data/knowledge/manifest.yml` and the five always-load entries (~964 lines) whole. `--entry <id>` prints one entry in full, custom entries under `<data>/knowledge/custom/` are marked `[custom]`, and the bold lead-ins of `learned-patterns.md` (210 lines) are listed so a session knows what is there to `Grep`.
- **`qualiow session <finalize|list|archive|delete|prune>`** — one command group for the session lifecycle. `finalize <dir|latest>` validates the session against `references/output-contract.md` — a confidentiality header on every `*.md`, `stats.json` strictly against `SessionMetricsSchema`, no unredacted secret in any `.md`/`.json`/`.log`/`.yml` outside `snapshots/` — then appends the `INDEX.md` row, the `all-bugs.md` rows and the `output/metrics.jsonl` line, creating the index files if they are missing. It is idempotent: a second run adds nothing. `--check` writes nothing and exits 1 with a numbered list of violations; `--redact` rewrites the offending files and names the categories it replaced. `archive <dir>` tars the directory (`--remove` also deletes it and marks the row `archived`); `delete <dir>` and `prune --older-than <days>` print the dry-run — size, file count, rows to remove — until `--yes`, and rewrite both index files rather than leaving dangling rows.
- **`qualiow list knowledge` flags** — `--domain`, `--tag`, `--type`, `--entry <id>`, `--changelog`, `--stats`. `--entry` prints the entry's raw YAML, which is how a session reads one large checklist without going through the manifest; `--stats` prints the manifest's `stats`, `active_releases` and `loading_strategy` counts.
- **`bin/qualiow`** — a launcher shim, so `${CLAUDE_PLUGIN_ROOT}/bin/qualiow` works in a plugin install. A plugin installed from git has no `dist/` and no `node_modules` (tsup does not bundle dependencies), so the shim runs the local build when the checkout has one and otherwise `npx`-fetches `qualiow-exploratory-testing` pinned to the version in `.claude-plugin/plugin.json`, falling back to `@latest`. `package.json` `bin.qualiow` still points at `dist/cli/index.js`, so npm installs are unchanged. `scripts/check-pack.mjs` asserts it ships.
- **`.claude-plugin/marketplace.json`** — the repository is its own marketplace, one plugin with `source: "./"`. Install with `claude plugin marketplace add willcoliveira/qualiow-exploratory-testing-skills` then `claude plugin install qualiow@qualiow`; `claude --plugin-dir <repo>` remains the development path. The install copies the whole repository (`src/`, `tests/`, the POC targets) — nothing private travels with it, since `data/targets/local-*.yml`, `.auth/` and the `.env` files are gitignored. Rationale, the release order it imposes and the ISSUE-001 constraint on agents: `docs/ARCHITECTURE-DECISIONS.md` ADR-012.
- **`qa-explore/references/delegation-rules.md`** — what the CLI owns, what stays with the model, and the **never-delegate list**: severity, priority, business impact, the bug reports, the charter and risk ranking, what is missing, the AC verdicts, the executive summary and the reflection. Carries the read thresholds (300 lines; a diff over 25 files or 1,500 lines), what a delegated read must return, and the override switches. Linked from `qa-explore/SKILL.md`.
- `snapshots/` in the session directory — raw page snapshots kept as working files, never part of a report and excluded from the `finalize` secrets scan. `qualiow explore` and the setup phase both create it.
- `loading_strategy.by_skill` in `data/knowledge/manifest.yml` — optional, drives `kb digest --for`; `qualiow validate` cross-checks its entry ids the same way it checks the rest of the loading strategy, and `qualiow kb sync` preserves the node.

### Changed

- **The setup phase loads the digest.** `qa-explore/phases/00-setup.md` Step 3 runs `qualiow kb digest --for explore --domain <domain>` (plus `--tag security` when the target has auth, `--tag data-integrity` for transactional flows) and reads only that; a specific entry comes from `--entry <id>` or a `Grep` of the release file, never a whole-file read of `manifest.yml`. `qa-verify-backend/SKILL.md` loads `--for backend` the same way, with its knowledge table kept as the reference list.
- **The reporting phases finalize through the CLI.** `qa-explore/phases/07-reporting.md`, `qa-explore-mobile/phases/07-reporting.md`, `qa-verify-backend/phases/05-reporting.md` and `qa-explore-quick/SKILL.md` end with `qualiow session finalize <session-dir>` in place of hand-written INDEX and all-bugs rows and a manual redaction scan. The bug reports, `session-report.md` and `stats.json` are still written by the session itself.
- **`/qa-knowledge-list` and `/qa-explore-cleanup` are CLI wrappers.** Knowledge-list maps its flags onto `qualiow list knowledge …` and prints the output verbatim instead of reading the manifest. Cleanup wraps `qualiow session list|archive|delete|prune` and keeps its rules: `delete` and `prune` show the dry-run, **ask**, and only then re-run with `--yes`. Both drop the tools they no longer use (`Write`, `rm`, `tar`, `du`).
- **`qualiow init` copies every agent** in the shipped `agents/` tree instead of one file named in the source, and filters `bin/qualiow` out of the `qa/bin/` copy — the shim belongs to a plugin install or a checkout, not next to the npm bin in a consumer project.
- `qa-explore/references/paths.md` gains a **CLI** section: `qualiow` on `PATH` → `npx -y -p qualiow-exploratory-testing qualiow` → `${CLAUDE_PLUGIN_ROOT}/bin/qualiow` → `bin/qualiow`, under the same rule as the mobile driver — resolve once, then write the resolved literal prefix in every command. The data-directory section records that the CLI resolves the same order (`--data` → `$PWD/data` → `$CLAUDE_PLUGIN_ROOT/data` → the packaged data), which is the new `resolveDataDir` export.
- `qa-explore/references/output-contract.md` lists `snapshots/*.yml` among the session files and states that the index rows are written by `qualiow session finalize`.
- CI validates **both** plugin manifests — `claude plugin validate .` and `claude plugin validate .claude-plugin/marketplace.json` — and the shell-syntax and packaging steps cover `bin/qualiow`; the end-to-end `init` step asserts the shim is absent from `qa/bin/` and exercises `kb digest` and `session list`. `scripts/sync-version.mjs` now keeps `marketplace.json` in step with `package.json` as well as `plugin.json`.

### Fixed

- **`npx qualiow` was the wrong package name.** The published package is `qualiow-exploratory-testing`; `qualiow` is only the bin name, and `npm view qualiow` is a 404. A bare `npx qualiow` in a project without the package installed therefore fetched nothing of ours. `/qa-knowledge-add` and every fence that invokes the CLI now write `qualiow …` with the resolution order from `paths.md`, or the explicit `npx -y -p qualiow-exploratory-testing@<version> qualiow …`.
- **`qualiow init` copied the sub-agent by hard-coded file name**, so any agent added later would silently never reach an init'ed project; the plugin-manifest test and the CI end-to-end step hard-coded the same name and would not have caught it.
- **`stats.json` is validated strictly**, as `output-contract.md` has claimed since 2.0.0: `SessionMetricsSchema` is applied with `.strict()` at `finalize`, so an unknown key is a numbered violation instead of being accepted silently.
- `/qa-knowledge-add` reads the manifest version with a `Grep '^version:'` rather than reading the file.

### Release order

`npm publish` must land **before** `git push --follow-tags` and the GitHub release: `bin/qualiow` pins its `npx` fetch to the version in `.claude-plugin/plugin.json`, so a tag that arrives first leaves plugin installs falling back to `@latest`, which is the previous release.

## [2.0.0] - 2026-09-08

A review of the 1.3.0 package (source, skill content, docs, packaging) verified against Playwright 1.63, `@playwright/cli` 0.1.19, the current Claude Code skill/sub-agent/plugin docs and Maestro 2.10 found sixty defects; this release fixes them and unifies the contracts the skills, the CLI and the formatters share. It is a **breaking** release — the migration notes are at the end of this section.

### Breaking

- **`qualiow init` now actually installs skills from the npm package.** Every 1.x release copied from `<pkg>/.claude/skills`, a directory the tarball never contained, so `init` installed nothing. It now copies the shipped `skills/` tree (falling back to `.claude/skills/` in a git checkout), the `qa-gather-agent` sub-agent, the data files, the mobile driver into `qa/bin/`, and `qa/.env.example`. It no longer writes a root `.env.example`, no longer adds `dist/` to `.gitignore`, respects `--force` uniformly (1.x overwrote `data/targets/_default.yml` unconditionally), merges `.gitignore` by exact line (1.x used substring matching, so an existing `.env.example` suppressed `.env`), never copies `local-*.yml`, and is idempotent. New `--dry-run`.
- **One session-directory scheme for every session kind:** `output/sessions/<YYYY-MM-DD-HHmm>-<kind>-<slug>/`, `kind ∈ explore|quick|mobile|backend`. 1.x used four incompatible patterns and `qualiow explore` a fifth that sorted after every skill-created directory, so `report -s latest` resolved to the empty CLI stub.
- **Quick sessions emit the standard artefacts** (`session-report.md`, `bugs/BUG-NNN.md`, `stats.json`, an INDEX row) instead of `quick-report.md`, so `/qa-explore-report`, `/qa-explore-feedback`, `/qa-explore-cleanup` and the CLI can see them.
- **One bug-report and session-report contract** shared by the templates, all four session skills, the fixtures and the parser: `# BUG-NNN: [Component] fails [Condition] causing [Impact]`, `**Severity:**`/`**Priority:**`/`**Component:**`/`**URL:**`/`**Environment:**`/`**Reproduction rate:**`, the coverage-map header `| Area | Risk | Status | Bugs | Notes |`, and a two-line confidentiality blockquote as the first lines of every artefact. 1.x had three incompatible formats and the shipped template could not be parsed (title became "Bug Report", severity always `medium`).
- **`output/sessions/INDEX.md` and `output/bugs/all-bugs.md` headers** are `| Date | Kind | Target | Bugs | Duration | Status | Report |` and `| ID | Session | Title | Severity | Status | Report |`; no placeholder rows (`list sessions` counted them).
- **Phase artefacts are numbered after their phase**: `phase-3-discovery.md` … `phase-6-edge-cases.md` (1.x wrote `phase-1-discovery.md` from phase 3 and the report skill looked for files nothing produced).
- **`stats.json` is the `SessionMetricsSchema` shape** (extended with optional `kind`, `coverage`, `evidence`, `areas_not_tested`, `blocked_by`), and `qualiow report` records it in `output/metrics.jsonl` once per session.
- **Target configs are validated strictly**: unknown keys anywhere fail, `.parse()` returns the validated object (1.x accepted and passed through anything), and a target's or domain's `id` must equal its file name. The `_example-*` and `_default` ids were renamed accordingly (`_default`, `_example-mobile-emulation`, `_example-native-mobile`, `_example-sim-ios-safari`, `_example-sim-android-chrome`).
- **Domain configs are YAML only.** `DomainConfigSchema` now matches the shape every file always had (`risk_ranking` p0–p3 map, `journeys[{name, steps}]`, `must_test_patterns` map, `common_bugs`, `compliance`, `guidance` string); the six `data/domains/*.md` files are removed and every skill reads `.yml`. `npm run validate` passes for the first time since the domain schema was written.
- **`qualiow gather` is removed** (it printed "output will be saved" and wrote nothing). Use `/qa-gather`.
- **`qualiow explore` is labelled what it is**: pre-flight only — validates inputs, creates the skeleton, prints the `/qa-explore … --session <dir>` command. `--time-box` defaults to and is capped at `45m`.
- **`qualiow report -f md`** writes `session-summary.md` (1.x wrote nothing for the default format). New `-o <file>` and `--stdout`; a partial `-s` match must be unique.
- **Node ≥ 22.4** (`engines`); `bin/wkeval.mjs` uses the global `WebSocket` and `fs.cp` is stable only from 22.3.
- **Mobile toolchain scripts moved** from `scripts/` to `bin/` (`bin/setup-mobile.sh`, `bin/doctor-mobile.sh`) and are exposed as `qualiow-setup-mobile` / `qualiow-doctor-mobile`; the driver and wrappers are exposed as `mcli`, `wadb`, `wk-ios` package binaries. The skill no longer uses `$MCLI` / `export MOBILE_CLI_STATE` (first-token permission rules never matched them); state is per-invocation via `--state <file>`.
- **`playwright-cli network` → `requests`.** The `network` command never existed; a quick session errored on its fourth command.
- **`.npmignore` removed** (with `files` present it was ignored by npm and its comments described a policy that was not applied). Sourcemaps no longer ship (they embedded all of `src/`).
- Library: `DomainConfig`/`Journey` types changed to the house shape; `ParsedBug` gained `priority`, `environment`, `reproduction_rate`, `summary` and `evidence.{videos,logs,network_failures}`; `redact()` output format is `key<sep>[REDACTED]` (key and separator preserved) and category names changed.

### Added

- **Claude Code plugin manifest** `.claude-plugin/plugin.json` (`name: qualiow`): `claude --plugin-dir <repo>` or a marketplace install exposes the skills as `/qualiow:qa-explore` etc. `skills/` and the new `agents/` are the generated mirrors of `.claude/skills` and `.claude/agents` (`npm run sync:plugin`, `npm run check:mirror`; CI fails on drift).
- **Project-local configuration** completed: `qa/target.yml` and `qa/.env` are honoured by every session skill and by the CLI (`resolveTargetPath`: `--target` → `qa/target.yml` → `_default.yml`); `/qa-target-setup` writes `qa/target.yml` by default (`--shared` for `data/targets/`); `qa/.env` is gitignored by `init`; `qualiow validate` checks it. The resolution order is written once in `references/paths.md`.
- **`references/output-contract.md`** (bug/session-report/stats/INDEX contract) and **`references/security-rules.md`** as the single rule set: one production rule (`prod|production|prd|live` against the hostname with an explicit exclusion list — 1.x tripped read-only mode on `staging.example.com/products`), one redaction list, one confidentiality header, and session isolation (`-s=<kind>-<HHmm>-<slug>` on every command, `close` then `delete-data` at the end — mandated in 1.x, executed nowhere). Every skill links it.
- **Redaction is wired in.** `redact()` was exported but never called; every formatter now redacts the report body and every bug field. New categories: bare JWTs, `Authorization`/`Set-Cookie` headers, AWS access/secret keys, `sk-`, GitHub, Slack tokens, private-key blocks, emails (except `example.*`/`localhost`); key/value patterns require an actual `=`/`:` so "Password field accepts…" and "the secret sauce" are left alone; card numbers are Luhn-checked so timestamps and order ids survive.
- **Confidentiality header on every output**: templates, session artefacts, the HTML report (banner + `Content-Security-Policy` and `noindex` meta), JSON (`meta.classification`), Jira CSV (note in every description). Jira cells are formula-neutralised (`= + - @` prefixed with `'`).
- **Knowledge-base integrity**: `KnowledgeReleaseSchema`, `KnowledgeChangelogSchema`, `validateKnowledgeBase()` (registry ↔ entry files ↔ stats ↔ `loading_strategy` ↔ changelog), `qualiow kb sync|check`, `scripts/kb-sync.mjs`. The manifest registry had 27 of 29 entries (both v0.6.0 entries were invisible) and `v0.1.0/release.yml` declared 6 of its 13.
- **CLI** `validate --kb`, `report -o/--stdout`, `init --dry-run`, `list` shows `qa/target.yml` and unindexed session directories; `qualiow --version` reads `package.json` (1.x hard-coded `1.0.0`).
- **Mobile driver hardening** (`bin/mobile-cli.mjs`): Maestro flows are built from JSON-escaped scalars (text containing a quote and a newline could inject a `launchApp` step; `tap-id`/`fill-id` escaped nothing); flow files go to a `0600` `mkdtemp` directory instead of a predictable world-readable `/tmp` path; refs expire 60 s after the snapshot that produced them and off-screen nodes get no ref; platform detection uses `adb devices` and `simctl list devices -j` (an iOS simulator *name* was classified as Android by string length); exit codes 0/1/2/3/4; new `boot` (adopts an already-running emulator/simulator instead of spawning a second one), `version`, `info` documented with `tap-id`, `fill-id`, `wait-text`, `logs-clear`; Android recordings are real `.mp4` files. `bin/doctor-mobile.sh` checks Node ≥ 22.4, `python3` and Maestro ≥ 2.6; `bin/setup-mobile.sh` pins `MAESTRO_VERSION` (default 2.10.0) and refuses `curl | bash` under `--yes` without `--allow-curl-bash`.
- **playwright-cli 0.1.16–0.1.19 features** adopted in the skills: `find "<text>"`, `open --mobile --device=`, `requests --filter=`, `video-chapter`/`video-show-actions`, `recording-start/stop`, `--raw snapshot` diffs, `run-code "async page => …"` for the Playwright-library observation helpers (`consoleMessages()`, `pageErrors()`, `requests()`).
- **CI** (`.github/workflows/ci.yml`, ubuntu + macOS): lint, build, mirror check, tests, `validate --all`, `kb:check`, tarball assertions (`scripts/check-pack.mjs`), an end-to-end `qualiow init` into a fresh project (second run must be a no-op), shell syntax, plugin manifest. Dependabot for npm and actions.
- **Tests**: 98 → a suite that validates the real `data/` tree, the knowledge base, `init` in a temp project, redaction gaps and false positives, the canonical fixtures, formatters, session-dir/table/gitignore helpers, the mobile flow builder (injection cases), the skills mirror and a skills lint (frontmatter, forbidden strings, link resolution, `allowed-tools` coverage of every first token in every bash fence).

### Fixed

- `page.accessibility` is described as removed in Playwright 1.57 (not deprecated); `npx playwright trace` uses the `open` subcommand; `init-agents --loop=claude` is described as generating `.claude/agents/` and `.mcp.json` (not `tests/agents/` or a config rewrite), loops `claude|codex|copilot|opencode|vscode|vscode-legacy`; `page.pickLocator()` marked human-only.
- `allowed-tools` now covers every command the phases run (mobile: `qa/bin/mcli`, `bin/mcli`, `mcli`, `${CLAUDE_SKILL_DIR}/../../bin/mcli` and the same for `wadb`, `wk-ios`, `doctor-mobile.sh`; backend: `node`; target-setup: `npx playwright`).
- Phase links use `${CLAUDE_SKILL_DIR}/phases/…`; broken relative links in `mobile-edges.md` and four `qa-verify-backend` files; the report skill's phase-file list; `press HOME` works on iOS; the mobile `logs/` directory is created; `qa-verify-backend` summary line lists all six verdicts and its knowledge list includes the two v0.6.0 entries; `04-e2e-trigger` reads `environment.kind`, `00-setup` reads `api.browser_profile`; the `qa-gather-agent` sub-agent is referenced by `/qa-gather` and reads `.yml` domains; the `tool-qa-workflow` name is gone.
- Time budgets sum to 45 minutes (1.x allocated ≥ 50).
- Formatters parse the coverage header the skills actually write and no longer shift columns on empty cells; `extractDomain` dead parameter removed; `metrics.jsonl` tolerates a corrupt line.
- Data: `.env.example` documents the variables the targets use (`QA_USER`, `QA_PASS`, `QA_TOKEN`, `QA_AWS_PROFILE`, `QA_AWS_REGION`, `QA_API_TOKEN`, `EXAMPLE_API_KEY`); `changelog.yml` v0.2.0 block completed and entry names aligned with the entry files; `_example-native-mobile.yml` names the standard `qa-iphone` simulator; `technique-business-logic-race-conditions` no longer recommends the non-existent `network` command.
- Packaging: `data/security/` and `data/targets/_example-api-only.yml` ship (both were referenced by shipped docs and absent); the CLI shebang is injected by the build rather than relying on the first line of a source file; `tsconfig` type-checks the tests; `@playwright/cli` bumped to `^0.1.19`, which also closes KNOWN-ISSUES ISSUE-002 (negative positional args).
- Docs: package name (`qualiow-exploratory-testing`, not `@qualiow/exploratory-testing`), CLI flags and honest command status, session paths, the Homebrew cask name (`android-commandlinetools`), env var names, phase count, shipped targets; `PROJECT-STATUS.md` and `PRODUCTION-READINESS-REVIEW.md` removed (their open items live in `KNOWN-ISSUES.md`); `CHANGELOG` 1.3.0 errata below.

### Errata for earlier entries

- Commit `db5c923` (2026-09-04) shipped on `main` after 1.3.0 without a version, tag or changelog entry: `api.auth: api-key-env` + `api.header_name`, `data/targets/_example-api-only.yml`, the `.env.example` API-key block, and knowledge base v0.6.0 (`technique-exactly-once-verification`, `technique-async-callback-contracts`). It is part of 2.0.0.
- 1.3.0: `/qa-verify-backend` has four lanes (the entry says three, then adds a fourth); it shipped 7 phase files and 6 references (not 6 and 2); the knowledge base reached 27 entries in 1.3.0 and 29 with v0.6.0. 1.0.0: nine skills were listed under a "10 skills" heading.

### Migrating from 1.x

1. Re-run `qualiow init` in each project (it is safe; use `--force` to take the new skill versions over locally edited copies). Move `.env` values you keep per project to `qa/.env`.
2. Rename any tooling that read `quick-report.md`, `phase-1-discovery.md`, the old INDEX columns, or session directories without a `-<kind>-` segment.
3. Targets whose `id` differed from the file name must be renamed (`qualiow validate` tells you which).
4. If you referenced `data/domains/<domain>.md`, read the `.yml`.
5. `scripts/setup-mobile.sh` → `bin/setup-mobile.sh` (or `qualiow-setup-mobile`); `scripts/doctor-mobile.sh` → `bin/doctor-mobile.sh`.
6. Library consumers: `DomainConfig`, `Journey`, `ParsedBug`, `SessionMetrics` and `redact()` changed as listed under Breaking.

## [1.3.0] - 2026-09-03

All changes in this release are **additive and backward-compatible** with v1.2.0. No skill names, frontmatter fields, CLI commands, bin entries, library exports, or `files` whitelist entries were renamed or removed.

### Added — Backend & infrastructure AC verification (`/qa-verify-backend`)
- New skill `skills/qa-verify-backend/` (SKILL.md + 6 phase files + 2 references): verifies acceptance criteria that have **no UI surface** — tables and streams, queue consumers, Lambda triggers, IAM policies, webhooks, IaC. Three lanes: **static** (reads the implementation branch against each AC with `git show`, never checking out), **live** (read-only `aws-cli` probes, one per AC, raw output saved as evidence), and **end-to-end** (drives the real write path in a non-production environment, then re-probes the data layer — including same-tick writes, deletes, bulk saves, a second identity, and DLQ depth).
- Output is an **AC traceability matrix** — `PASS` / `PARTIAL` / `FAIL` / `BLOCKED` / `UNVERIFIABLE`, each with cited evidence — plus one bug report per finding. Every verdict names the observation mode that produced it; a verdict backed only by a code reading is `UNVERIFIABLE`, never `PASS`. `BLOCKED` is a first-class outcome: missing credentials produce ready-to-run probe commands rather than a verdict inferred from source.
- `references/aws-readonly-probes.md` — probe catalogue per resource type (DynamoDB streams and key schema, Lambda event source mappings, IAM `simulate-principal-policy` for both allows and denies, SQS DLQ depth, CloudWatch metrics and log hygiene, Terraform) with guidance on reading each output.
- `references/safety-rules.md` — read-only discipline, the production hard stop, account confirmation before the first probe, destructive runbooks treated as findings rather than instructions, redaction before disk, and untrusted-content handling. Extends `data/security/SECURITY-POLICY.md`.

### Added — The API verification lane (`phases/03b-api-verification.md`)
- A fourth lane for the surface most backend tickets actually have: **the service's own HTTP endpoints**. The request runs inside the already-authenticated page (`playwright-cli eval` + `fetch(…, {credentials: 'include'})`), so it carries the same session cookie, CSRF token and client interceptors as the UI — no token plumbing, no stored credential, and it works with SSO/MFA that no scripted login can pass. Read-only in every environment, limited to the endpoints the target's `api.probe_allowlist` declares.
- **Environment fingerprinting runs before the first probe of any lane** (phase 0 step 3c, `references/environment-fingerprinting.md`). Which build is deployed in every component of the request path; whether the changed code path is *selected* here — a flag, a config value or a routing rule can pick between two implementations of one feature inside a single identical build; and whether the commit under test is genuinely an ancestor of what is running (`git merge-base --is-ancestor`). *Same build, different behaviour ⇒ configuration, not deploy lag.* Includes behavioural fingerprinting for services with no version endpoint.
- New verdict **`NOT-REACHABLE`** — this environment does not run the changed code path. Not a pass and not a failure, and the honest answer to "it works in dev" when the flag is off everywhere else. Every verdict is now scoped to the environment it holds in.
- **The UI-vs-API differential pass.** When a ticket has both a screen and an endpoint, the overlapping cases run at both surfaces and every finding is sorted into *both* (fix it in the service), *API only* (a real defect the client's guard is hiding, reachable by every other client), *UI only* (the client invents or masks behaviour the service does not have) or *neither*. This closes the most common false `PASS` in exploratory testing: recording a client-side guard as evidence that the endpoint behaves correctly.
- `references/api-probes.md` — getting an authenticated request context in three modes and what each one actually proves; ten case families to fire at any endpoint (length boundaries, tokenisation, metacharacters, the four kinds of nothing, enum values, pagination bounds, type confusion, casing, second identity, second scope); a signal-to-meaning table for reading results; evidence hygiene.
- New template `data/templates/api-probe-matrix.md` — environment fingerprint, case table with a column per environment, four-bucket findings table, evidence index.
- Cross-environment rule enforced throughout: **compare status codes and response shapes, never absolute counts.** Different environments hold different data and it drifts between runs.

### Added — Payload correctness and release-level verification
- `references/payload-verification.md` — **a well-shaped `200` is not a correct answer.** Every derived value (percentage, total, ratio, delta, aggregate) is recomputed from the raw figures in the same response, using the formula from the **specification** rather than from the code under test, with cases chosen to stress sign, zero, scale and cardinality, plus the structural invariants that hold regardless of magnitude. The report then states what the check does not prove — when both sides come from one payload the *derivation* is verified and the *inputs* are not — and names the **independent oracle** that would close the gap, along with whether it was run.
- The same reference covers **presentation integrity**: the payload held next to the screen, because a correct response still reaches the user as a wrong number when a formatter guesses what a value is (`value > 1 ? value : value * 100` is wrong for every value at or below the threshold it tests), a unit is applied twice, rounding crosses a threshold, or a truncated figure is shown as a total. Includes attributing the corruption to the change that owns it — usually not the change under test, and frequently one deployed on a single environment. Also: in-page cold/warm timing with `performance.now()`.
- `references/release-readiness.md` — verifying many tickets against one build. The deployment table comes first for everything (`git merge-base --is-ancestor`), so a ticket whose backend is not deployed is **not testable here** rather than tested against a UI that will render convincing nonsense. A fixed result vocabulary keeps *not testable here* and *not tested* visible; a four-state coverage map marks 🔍 *code-verified only* as `UNVERIFIABLE` rather than green; carry-over defects get their own section; a deploy landing mid-session is handled explicitly; and the report closes with a disposition and the condition that would reverse it.
- New template `data/templates/expected-behaviour.md` — the specification that should have existed, for the majority of API findings that have no acceptance criterion to be filed against. Observed against expected, grouped by cause rather than by case, with the decisions the fix forces made explicit (**reject, do not clamp**; an error rather than a silent zero; validation at the layer covering every implementation, never only the client), ranked by what real users can reach *today*, and closed with a plain-English reply for whoever decides to fund the work.
- `data/templates/coverage-map.md` gains the **code-verified-only** state and two distinctions that routinely produce a false pass: *consistent is not causal* (a setting whose value happens to match the output proves nothing until it is changed), and *the data has to reach the case* (an AC about negative values cannot be verified against records that are all zero).

### Added — Knowledge base v0.2.0 through v0.5.0 (13 → 27 entries)
- `technique-contract-narrowing` (v0.2.0) — verifying a swapped data source. When a full-record read is replaced by a projection, the request's field-selection list silently becomes the payload specification; unrequested fields vanish with no error, no DLQ, and a green suite.
- `technique-test-suite-audit` (v0.2.0) — auditing the branch's own tests. When a change rewrites the tests meant to prove it works, those tests become part of the change under review.
- `technique-verification-mode-selection` (v0.3.0) — routing each AC to the channel that can actually falsify it: API-behind-the-screen, direct request to a no-UI endpoint, LLM output judgement, or needs-a-human. Makes `UNVERIFIABLE` a first-class verdict.
- `technique-functional-diff-analysis` (v0.3.0) — eight passes that read a backend/API/LLM diff for behaviour at the service boundary rather than code quality, producing falsifiable hypotheses attached to ACs.
- `technique-llm-output-verification` (v0.3.0) — fixed input set, written rubric, before-and-after on the same inputs, N runs to expose variance, assertions on properties and tool trajectory rather than generated text.
- `technique-environment-fingerprinting` (v0.4.0) — proving what an environment actually runs before any verdict is written: build id per component of the path, whether the changed path is selected here, commit ancestry, and the verdict mapping that follows from the answers.
- `technique-authenticated-api-probing` (v0.4.0) — calling the endpoint behind the screen. Three ways to get a request context and what each proves, ten case families, a signal-to-meaning table, and the credential and volume rules that keep the lane safe.
- `technique-ui-api-differential` (v0.4.0) — the same matrix at both surfaces, sorted into four buckets. "API only" is not reassurance: the browser guard is the only thing preventing it, and no other client has one.
- `technique-silent-failure-audit` (v0.4.0) — seven shapes of failure that render as a legitimate empty, zero or neutral result, how to force each one, and why they recur as a class (no shared error-state component) rather than as individual bugs.
- `technique-derived-value-verification` (v0.5.0) — recomputing the number and naming what it does not prove; choosing cases that stress sign, zero, scale and cardinality; structural invariants; finding an independent oracle.
- `technique-presentation-integrity` (v0.5.0) — does the screen show what the service sent? The formatter that guesses, units applied twice, rounding across a threshold, truncation shown as a total — and attributing the corruption to the right change.
- `technique-expected-behaviour-specification` (v0.5.0) — writing the spec that should have existed, including the recurring decisions and the plain-English reply that gets the work scheduled.
- `technique-config-surface-verification` (v0.5.0) — when the configuration mechanism *is* the change: values matching the deployed config rather than the source default, nothing leaking alongside them, the runtime genuinely reading through the surface rather than around it, and the route existing in every environment the change will be promoted to.
- `technique-release-readiness-verification` (v0.5.0) — many tickets, one build: establish what is deployed before testing anything, keep not-testable and not-tested visible, mark a code reading differently from an observation, separate carry-overs, and close with a disposition and its reversal condition.
- `data/knowledge/learned-patterns.md` now carries a backend/API/event-driven section, an **HTTP endpoints** subsection, a **calculated values and what reaches the screen** subsection, a **reporting a set of tickets** subsection, a **findings with no acceptance criterion** subsection, a **triage and re-verification** section ("closed: no legitimate user vector" is a hypothesis to falsify; "partial fix" is the most common re-test outcome; re-verify against a build, not a date), and an expanded set of **disqualifiers** — findings that look real on a first read and do not survive tracing.

### Added — Configuration surface
- New target template `data/targets/_example-backend.yml` documenting the four optional blocks consumed by the skill: `environment:` (what the skill may do here), `backend:` (cloud resources to probe read-only), `api:` (the service's HTTP surface — auth mode, browser profile, version endpoint, endpoints, `probe_allowlist`, `parity_targets`, and where each feature flag's deployed value is declared) and `source:` (the implementation branch to review). Credentials are referenced by env var **name** or by gitignored profile directory; values stay in `.env`.
- New domain profile `data/domains/identity.{yml,md}` (identity and access platforms, and the admin consoles that configure them) — audit trail integrity, identity propagation, consent and privacy, configuration correctness, admin RBAC, log and data hygiene.
- New doc `docs/BACKEND-VERIFICATION.md` — the end-to-end workflow, the three lanes, the verdict vocabulary, and the safety rules worth knowing before a first session.
- `.env.example` documents `QA_AWS_PROFILE` / `QA_AWS_REGION`; `.gitignore` now excludes `data/targets/local-*.yml` and `output/context/*.md` so private target configs and gathered ticket content stay local.

### Changed
- Schema/library: new `EnvironmentConfigSchema`, `BackendConfigSchema`, `ApiSurfaceConfigSchema` and `SourceBranchConfigSchema`, with matching `EnvironmentConfig`, `BackendConfig`, `ApiSurfaceConfig` and `SourceBranchConfig` TS types. `WebTargetConfigSchema` accepts all four blocks as optional — a target without them is still a valid `/qa-explore` target, so existing configs are unaffected.
- `/qa-verify-backend` gains `--api-only` (skip the cloud lane) and `--parity <target-id>` (run the same API matrix in a second environment and compare shapes).
- `phases/05-reporting.md` gains a carry-over section, a four-state coverage map, and a step that produces an expected-behaviour specification where the findings have no acceptance criterion behind them. The disposition line now carries the scope of what was checked when it is narrower than the question being asked.
- `references/safety-rules.md` gains a session-credential rule (an authenticated profile or a cookie taken from it is a live credential and never enters a script, a committed file, a report or a message), makes the API lane read-only in every environment with an allowlist and a volume limit, and extends untrusted-content handling to API response bodies and error strings.
- `package.json` `files` now ships `data/targets/_example-backend.yml` and `docs/BACKEND-VERIFICATION.md`.

### Fixed
- `.gitignore` ignored `.auth/*.json` but not `.auth/<profile>/` directories. The API lane uses **persistent browser profile directories**, which hold live session cookies for a real account, so the pattern is now `.auth/*` with the `.gitkeep` placeholder negated. Without this, following the documented setup would stage a working credential.
- `package.json` `files` shipped `data/domains/*.yml` only, so no `data/domains/<domain>.md` has ever reached an installed copy — while `qa-explore` and `qa-verify-backend` both instruct reading that file for the domain's data-integrity checks. Now ships `data/domains/`, which also covers the new `identity.md`.

### Known issues
- `data/domains/identity.yml` fails `npm run validate`, in exactly the same way as the five domain files already in the repo (`_default`, `ecommerce`, `fintech`, `marketing`, `saas`): `DomainConfigSchema` has drifted from the shape every domain file actually uses (`risk_ranking` as a map, `journeys` without `id`/`description`/`risk`, `guidance` as a string). The new file follows the existing house format rather than a schema nothing conforms to. Reconciling the two is a separate change.

## [1.2.0] - 2026-07-09

All changes in this release are **additive and backward-compatible** with v1.1.0. No skill names, frontmatter fields, CLI commands, bin entries, or library exports were renamed or removed.

### Added — Mobile exploratory testing (`/qa-explore-mobile`)
- New skill `skills/qa-explore-mobile/` (SKILL.md + 8 phase files + `references/mobile-edges.md`): full exploratory sessions on iOS Simulators / Android Emulators, in two modes selected by the target config — **NATIVE** (an installed app is the system under test) and **WEB** (a mobile web app in the REAL device browser: iOS Simulator Safari / Android Emulator Chrome — engines Playwright cannot drive).
- New mobile driver `bin/mobile-cli.mjs` — a Maestro / `xcrun simctl` / `adb` shim mirroring the `playwright-cli` command surface (`set-device`, `set-app`, `launch`, `open-url`, `snapshot` with a11y refs, `click`, `fill`, `clear`, `press`, `screenshot`, `logs`, `record-start/stop`, `wait-text`, `tap-id`/`fill-id`). Node built-ins only, no install step. Wrappers: `bin/mcli` (resolves JAVA_HOME/ANDROID_HOME/PATH), `bin/wadb` (wrapped adb), `bin/wk-ios` + `bin/wkeval.mjs` (WebKit Remote Inspector JS eval in sim Safari via ios-webkit-debug-proxy).
- New scripts: `scripts/setup-mobile.sh` (idempotent toolchain bootstrap — Homebrew, Node, Maestro, JDK 17, Android cmdline-tools/platform-tools/emulator, Google-Play system image, standard AVD `qa_pixel_api35`, iOS sim `qa-iphone`; checks Xcode Command Line Tools and prints guided-manual steps for Xcode + the iOS runtime) and `scripts/doctor-mobile.sh` (read-only readiness preflight with per-item fix commands).
- New target templates: `data/targets/_example-native-mobile.yml` (native app), `_example-sim-ios-safari.yml` / `_example-sim-android-chrome.yml` (mobile web on real sim browsers), `_example-mobile-emulation.yml` (mobile web via Playwright device emulation — the `/qa-explore` path, no simulator needed).
- New doc `docs/MOBILE-SETUP.md` — fresh-Mac setup guide (device names, the two genuinely manual iOS steps, Play-image rationale, auth reality, troubleshooting).
- Schema/library: `MobileTargetConfigSchema` (+ device/app/web/source_repo/mobile-scope sub-schemas) and matching TS types; `TargetConfigSchema` now accepts web AND mobile targets, discriminated on the `platform` key with per-field error paths preserved. Auth strategies extended with mobile-only `in-app` and `interactive-sso` (+ `identity_provider`, `static_otp`, `test_email_pattern`). `BrowserConfigSchema` documents optional `engine`/`channel`/`device` for mobile-web emulation targets.
- `package.json` `files` now ships `bin/`, the two mobile scripts, the mobile target templates, and `docs/MOBILE-SETUP.md`.

### Fixed
- `data/targets/_default.yml` (ad-hoc target) no longer fails validation: `base_url` may be `''` when the URL is provided at session start.

## [1.1.0] - 2026-04-11

All changes in this release are **additive and backward-compatible** with v1.0.0. No skill names, frontmatter fields, CLI commands, bin entries, library exports, or `files` whitelist entries were renamed or removed. Consumers can upgrade from 1.0.0 → 1.1.0 without changing their workflows.

### Added
- New reference file `skills/qa-explore/references/playwright-agents-integration.md` documenting opt-in integration with the Playwright Test Agents framework (planner / generator / healer) introduced in Playwright 1.56. Includes a bug-to-regression-test workflow, prerequisites, limitations, and a feature cross-reference table.
- Optional peer dependency on `@playwright/test ^1.59.1` (declared with `peerDependenciesMeta.optional: true` so v1.0.0 consumers upgrading see no `EPEERINVALID` warning). Consumers who want to use Test Agents can install it; everyone else is unaffected.
- `qa-explore` phase files now surface Playwright 1.56–1.59 features where each is most useful:
  - `phases/00-setup.md` — new optional Step 6: bootstrap Test Agents via `npx playwright init-agents --loop=claude` when prerequisites are present.
  - `phases/03-discovery.md` — API-level observation helpers (`page.consoleMessages()`, `page.pageErrors()`, `page.requests()`) and a note on Service Worker network routing (all 1.56).
  - `phases/05-features.md` — locator stabilization helpers (`page.pickLocator()`, `locator.normalize()`, 1.59) and a Test Agents handoff recipe for converting reproducible bugs into regression specs.
  - `phases/06-edge-cases.md` — note that `page.accessibility` is deprecated in Playwright 1.57 and `page.ariaSnapshot()` (1.59) is the replacement.
  - `phases/07-reporting.md` — trace analysis callouts for `npx playwright trace` CLI (1.59), HTML reporter "Speedboard" timeline (1.58), Trace Viewer themes (1.58), and optional `page.screencast()` (1.59) for premium evidence on Critical/High bugs.
- `qa-explore-quick/SKILL.md` — inline bullet on `page.pickLocator()` / `locator.normalize()` for locator stability during 15-minute sessions.
- `qa-target-setup/SKILL.md` — new optional Step 9 offering to scaffold Test Agents while target credentials and scope are already loaded.
- `qa-explore/SKILL.md` — `Bash(npx playwright:*)` added to `allowed-tools` to permit `npx playwright init-agents` and `npx playwright trace` when used from any phase. Sessions that never invoke `npx playwright ...` are unaffected.
- `qa-explore/SKILL.md` References section now lists `playwright-agents-integration.md` alongside the existing references.
- New npm scripts: `sync:skills` (mirrors `.claude/skills/` → `skills/` via `rm -rf skills && cp -R .claude/skills skills`) and `prebuild` (runs `sync:skills` before every `tsup` build). The existing `prepublishOnly` chain automatically picks this up, so every publish ships a fresh mirror.

### Changed
- `@playwright/cli` dependency pinned from `"latest"` to `"^0.1.6"` for reproducible installs. The caret stays inside the 0.1.x line.

### Deprecated
- Use of `page.accessibility` for accessibility checks is discouraged in `phases/06-edge-cases.md` guidance. This is an upstream Playwright 1.57 deprecation; **no qualiow API is deprecated**.

## [1.0.0] - 2026-03-29

### Added
- 10 Claude Code skills for exploratory testing
  - `/qa-explore` — Full 45-min session with business context, risk ranking, data integrity
  - `/qa-explore-quick` — 15-min focused session
  - `/qa-explore-report` — Report generation from sessions
  - `/qa-explore-feedback` — Post-session learning
  - `/qa-explore-cleanup` — Session management
  - `/qa-gather` — Requirements analysis
  - `/qa-knowledge-add` — Knowledge base curation
  - `/qa-knowledge-list` — Knowledge browsing
  - `/qa-target-setup` — Target app configuration
- Decomposed skill architecture (8 phases + 4 references)
- 13 YAML knowledge entries (5 heuristics, 5 techniques, 1 checklist, 2 references)
- 5 structured domain configs (ecommerce, fintech, saas, marketing, default)
- TypeScript CLI with 6 commands (init, validate, list, report, explore, gather)
- Zod schema validation for all YAML configs
- Credential redaction utility (JWT, API keys, passwords, SSN, card numbers)
- HTML report generator (dark-mode, standalone, severity-colored bug cards)
- JSON and Jira CSV export formatters
- Session metrics collection (JSONL)
- Security policy (prompt injection resistance, credential protection, production safety)
- 83 unit tests passing
- Community-seeded learned patterns from 8 exploratory sessions
