@keber/qa-framework 1.12.0 → 1.13.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/CHANGELOG.md CHANGED
@@ -7,6 +7,346 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## [1.13.0] - 2026-11-04
11
+
12
+ Dated by its release, not by its bump. `package.json` was moved to `1.13.0` on 2026-10-16,
13
+ but the version was never tagged or published, so everything below ships together under
14
+ this number. The infrastructure fixes listed in Added and Fixed were written later, on the
15
+ branch that unblocked this release.
16
+
17
+ ### Added
18
+
19
+ - `templates/agents/qa-reporte-ejecucion.md` - new Claude Code agent that exports the ADO
20
+ test plan results, versions the raw export under `ado-export-<YYYY-MM-DD>/` and writes the
21
+ sprint `Reporte-de-Ejecucion-de-Pruebas-<project>-Sprint-<N>.md` (summary with executed,
22
+ success and coverage metrics, plus promoted findings).
23
+ - `templates/agents/qa-investigador.md` - new Claude Code agent that investigates one root
24
+ cause against the real environment. Its tools are generated from `parallelLanes`: each lane
25
+ with an `mcpNamespace` adds 15 Playwright MCP browser tools, a lane table and the mandatory
26
+ heartbeat protocol; with no MCP lane it gets a no-browser section instead.
27
+ - Agent template engine: `{{#if FLAG}}` / `{{#unless FLAG}}` blocks, single-pass placeholder
28
+ replacement and a guard that fails on any unresolved `{{...}}` token.
29
+ - New placeholders: `INVESTIGATOR_TOOLS`, `LANE_MCP_TABLE`, `APPROVAL_THRESHOLD_PERCENT`,
30
+ `APPROVAL_THRESHOLD_RATIO`, `DECIMAL_SEPARATOR`, `TC_ID_PATTERN`, `QA_BASE_URL`,
31
+ `LOGIN_PATH`, `TECH_STACK`.
32
+ - Config keys: `integrations.azureDevOps.sprintCycle.approvalThresholdPercent` (default 85),
33
+ optional `conventions.decimalSeparator` and `conventions.defectIdPrefix`. Documented in
34
+ `qa-framework.config.schema.json`, together with the `mcpNamespace` pattern.
35
+ - `qa-framework validate` checks present-but-invalid values of those keys and of
36
+ `parallelLanes`, and now finds the config under `qa/` as well as at the project root.
37
+ - **`scripts/lib/module-keys.js`** - single source of truth for how a module key maps
38
+ to a spec folder (prefixed) and to an automation folder (bare). Shared by `init.js`
39
+ and `validate.js` so the two cannot drift apart again.
40
+ - **`scripts/sync-version.js`** - now also rewrites `frameworkVersion` in
41
+ `qa-framework.config.json`, by field rather than by the loose `vX.Y.Z` prose pattern.
42
+ - **`test/module-keys.test.js`, `test/sync-version.test.js`** - unit coverage for the
43
+ key helpers plus a drift guard that fails CI whenever a published version surface
44
+ disagrees with `package.json`.
45
+ - **`.github/workflows/publish.yml`** - the suite now runs before `npm publish`.
46
+ `ci.yml` triggers on pushes to `main` and on pull requests, so it never sees a tag,
47
+ and publishing is triggered by a tag push: nothing was verified on the path that
48
+ actually reaches npm. The drift guard above only protects a release once it runs
49
+ there.
50
+
51
+ ### Changed
52
+
53
+ - `init` and `upgrade` generate six agents (was four) when `sprintCycle.enabled` is true.
54
+ Agents are rendered independently: an invalid lane config fails `qa-investigador` only,
55
+ the other five are still written and the command exits 1.
56
+ - As npm postinstall (`init --skip-if-exists`), an agent that cannot be rendered is reported
57
+ as a WARNING naming the agent and asking to fix the config and run
58
+ `npx qa-framework upgrade`; postinstall exits 0 so `npm install` does not fail. Explicit
59
+ `init` and `upgrade` keep exit code 1.
60
+ - `qa-informe-resultados` reads its approval threshold from config instead of a hardcoded
61
+ 85% and states the decimal separator rule.
62
+
63
+ ### Fixed
64
+
65
+ - `qa-informe-resultados` looked for `Reporte-de-Ejecucion-de-Pruebas-...` and pointed to the
66
+ `qa-ado-integration` skill, which does not produce that file. It now names the
67
+ `qa-reporte-ejecucion` agent, the canonical filename (active and historical sprint folders)
68
+ and accepts the older `Reporte-ejecución-de-pruebas-*` files.
69
+ - `templates/qa-framework.instructions.md` and `templates/qa-framework.rules.md` carried a
70
+ project-specific `DEF-SIS-NNN` example; it is now `DEF-{PREFIX}-NNN`.
71
+ - **`scripts/init.js`** - module spec folders are now scaffolded as
72
+ `qa/01-specifications/module-{name}/submodule-{name}/`, the layout declared by every
73
+ skill, by `docs/folder-structure-guide.md` and by the `specPath` field of
74
+ `qa-framework.config.json`. init.js wrote the bare key, so a freshly scaffolded
75
+ project violated the spec-path convention its own agent instructions impose.
76
+ Prefixing is idempotent: a config whose key already reads `module-suppliers` is not
77
+ turned into `module-module-suppliers`.
78
+ - **`scripts/init.js`** - automation stubs and test case ids keep using the **bare**
79
+ key (`e2e/tests/suppliers/create.spec.ts`, `TC-SUPPLIERS-CREATE-001`), matching the
80
+ `automationPath` field and the live consuming projects. Only the spec tree is
81
+ prefixed.
82
+ - **`scripts/validate.js`** - `--strict` strips the `module-` / `submodule-` prefix
83
+ before deriving the automation path. It was pointing the warning at
84
+ `e2e/tests/module-{name}/`, a folder no project has ever created.
85
+ - **`qa-framework.config.json`** - `frameworkVersion` was stuck at `1.10.0` while the
86
+ package shipped `1.12.0`. The file is published and is the template copied into every
87
+ consuming project, where the value is injected into `{{VERSION}}` of the generated
88
+ agent instructions, so every fresh install since 1.11.0 reported the wrong version.
89
+ - **`package.json`** - the npm `version` lifecycle script now also stages
90
+ `qa-framework.config.json`. sync-version.js rewrites it, but the staging list did not
91
+ include it, so the release commit and its tag would have carried the OLD
92
+ frameworkVersion - and the publish workflow builds from a clean checkout of that tag,
93
+ reintroducing the exact drift this change set out to fix.
94
+ - **`test/sync-version.test.js`** - two tests compared files against their starting bytes,
95
+ which assumed the tree was already synced. On a drifted tree one of them ran
96
+ `sync-version` and left `qa-framework.config.json` rewritten: the first run failed, the
97
+ retry passed, and the drift vanished from the working tree unnoticed. A suite that edits
98
+ tracked files turns a red run green on the retry and hides what it exists to catch.
99
+ - **`test/init.test.js`** - the agent summary assertion matched only the forward-slash form
100
+ of `.claude/agents/` and took the first matching line. `init.js` prints those lines
101
+ through `path.relative`, so on Windows the per-file `[created]` lines never matched and it
102
+ landed on the summary line, while on Linux it landed on the first `[created]` line. The
103
+ test passed locally and failed the first time it ran on CI.
104
+
105
+ ---
106
+
107
+ ## [1.12.0] - 2026-09-08
108
+
109
+ Reconstructed from `4fe32fb..f52257a` (39 commits). Minor rather than major: a project
110
+ with no `parallelLanes` block behaves exactly as before, `PENDING-BROWSER` adds an enum
111
+ value without removing any, and the spec-path change affects what `init` generates from
112
+ then on, not what already exists. The one user-visible break is `engines` moving from
113
+ `>=18` to `>=20`.
114
+
115
+ ### Added
116
+
117
+ - **`parallelLanes` lane table** - `qa-framework.config.json` gains a lane block and
118
+ `qa-framework.config.schema.json` is published alongside it. A lane is four coupled
119
+ values (account env prefix, `storageState`, MCP namespace, runner project), none of
120
+ which can be inferred from the others, plus per-lane capabilities: a project can have
121
+ six accounts but only three MCP servers. A project with no block, or `enabled: false`,
122
+ resolves a single implicit default lane.
123
+ - **`templates/automation-scaffold/scripts/lane-lock.js`** - config-driven lane lock
124
+ ported from the reference implementation. `acquire` (random free lane, queues) and
125
+ `reserve` (exact lane, no queue, throws when busy) stay two separate verbs; staleness
126
+ is a composite predicate (past the threshold AND no recent heartbeat); the lock
127
+ directory resolves by shell > `.env` > per-checkout precedence and the resolved path
128
+ and its source are reported on stderr. Reads `.env` with `dotenv.parse()`, never
129
+ `config()`, so a script whose only job is choosing a directory never gains ambient
130
+ access to the QA account passwords in the same file.
131
+ - **`templates/automation-scaffold/scripts/global-setup-guards.js`** - `global-setup`
132
+ performs the only login, so it is the last point at which a run about to drive an
133
+ account it does not hold can be stopped. `assertColdSetupIsIntentional` throws before
134
+ any login when neither `QA_LANE_ONLY` nor `QA_COLD_SETUP=1` is set, since a cold setup
135
+ re-logs in every lane and invalidates other running sessions; `assertLaneIsLocked`
136
+ verifies outside CI that the lane is held; `assertProjectsMatchLane` catches a
137
+ `--project` whose `storageState` points at a different lane than the one reserved.
138
+ Plain CommonJS so it is testable with `node --test` without adding `ts-node`.
139
+ - **`templates/automation-scaffold/scripts/lane-config.js`** - lane table resolver shared
140
+ by the config and the guards.
141
+ - **`templates/qa-framework.rules.md`** plus **`templates/agents/`** - four agent
142
+ templates (`qa-analisis.md`, `qa-asesoria.md`, `qa-plan.md`,
143
+ `qa-informe-resultados.md`) for analysis, sprint advisory, test planning and ADO result
144
+ reporting, deployed through `scripts/lib/claude-agents.js` and
145
+ `scripts/lib/claude-commands.js`.
146
+ - **`scripts/check-forbidden-chars.js`** (`npm run check-chars`) - enforces the BLOCKING
147
+ character-safety rule the package stated but never checked. Flags em-dash, en-dash,
148
+ ellipsis, smart quotes and arrows; leaves Latin Extended and emoji alone. Reports
149
+ `file:line:column` with the code point and its ASCII replacement.
150
+ - **`scripts/check-spanish-accents.js`** (`npm run check-accents`) - language-aware accent
151
+ gate for Markdown. Classification is the hard part, not detection: it measures the
152
+ density of Spanish function words in prose only, after stripping fenced code, inline
153
+ code, link targets, tags, placeholders and paths. Against this package's corpus English
154
+ lands at 0.000-0.001 and Spanish templates at 0.125-0.165, with the threshold at 0.07;
155
+ a test asserts the gap stays wide.
156
+ - **`scripts/sync-version.js`** - wired into the `npm version` lifecycle to keep
157
+ `README.md` and `.github/copilot-instructions.md` aligned with `package.json`.
158
+ - **Test suite and CI** - black-box tests for `cli.js`, `init.js`, `validate.js`,
159
+ `upgrade.js`, the lane scripts and both character gates, using Node's built-in runner
160
+ with no new devDependency, plus `.github/workflows/ci.yml`.
161
+ - **`scripts/upgrade.js` section 9** - carries the v1.12.0 scaffold into existing
162
+ projects. `upgrade` force-copied only `.github/skills/`, `copilot-instructions.md` and
163
+ `QA-STRUCTURE-GUIDE.md`, so everything this release changed under `templates/` reached
164
+ new projects through `init.js` alone and existing installations kept the old scaffold,
165
+ including the login that types the password with `fill()`. The rule is per file:
166
+ overwrite what is framework-owned, warn and continue where overwriting would destroy
167
+ working user code, and treat a dependency group as all-or-nothing, since
168
+ `playwright.config.ts` and `global-setup.ts` both `require()` the lane scripts and a
169
+ partial install leaves a project that cannot start Playwright. Baseline detection is
170
+ content-hash based, not `frameworkVersion` based: the recorded values in live projects
171
+ were `1.10.0`, `1.1.3`, `1.1.3` and `1.0.0`, two of them malformed. Files left
172
+ user-owned are scanned for `.fill(password)` and reported with file and line, without a
173
+ textual patch.
174
+ - **`PENDING-BROWSER`** - fourth value in the TC Origin vocabulary, for an element
175
+ observed and confirmed to exist whose behavior could not be exercised end to end.
176
+ Without it agents either overclaim with `UI-OBSERVED` or misuse `PENDING-CODE`, which
177
+ pollutes gap tracking downstream.
178
+ - **Known-issues lookup before defect creation** - nothing required checking whether a
179
+ defect had already been filed. The gate distinguishes the two outcomes that matter: an
180
+ open match is referenced instead of refiled, a closed match is filed as a regression.
181
+ `templates/defect-report.md` gains the section that records the search.
182
+ - **Stage 5 gates** - `tsc --noEmit` plus a single smoke run of a newly written spec
183
+ before the submodule is marked complete. Both run before the completion checklist; a
184
+ full green suite remains Stage 5b's job.
185
+ - **Closing measurement for doc-generating stages** - the four skills that write Markdown
186
+ now end on evidence (accent count, forbidden characters, BOM) rather than a compliance
187
+ claim, with the language boundary stated so English artifacts are not asked for accents
188
+ they should not have.
189
+ - **`skills/qa-automation/references/pom-template.md`** - page-object template taken from
190
+ real project use: an abstract per-module CRUD base plus thin per-submodule classes,
191
+ with the accent-insensitive label matching that pattern needs against Spanish UIs.
192
+ - **`MIGRATION-NOTES.md`**, **`docs/decisions/lane-heartbeat-caller.md`** and
193
+ **`docs/visuals/`** - embedded-QA migration guidance, the record that the dispatcher
194
+ (not the executor) calls heartbeat, and the diagrams referenced from the architecture
195
+ docs.
196
+
197
+ ### Changed
198
+
199
+ - **`engines`** - `>=18` becomes `>=20`, and CI tests Node 20, 22 and 24. The matrix had
200
+ been testing 18 and 20, both out of maintenance, while `publish.yml` released on 24,
201
+ which no job tested. Users on 18 now get an npm warning, which is the point.
202
+ - **`templates/*/playwright.config.ts`** - both configs called `dotenv.config()`, which
203
+ injects the entire `.env` into `process.env`. In a real project that file holds every QA
204
+ account password plus ADO tokens (82 lines in one case) while these configs need a
205
+ handful of non-secret values. They now read an explicit key list with `dotenv.parse()`,
206
+ which returns a plain object and never touches `process.env`, and only fill keys the
207
+ environment has not already set, so shell and CI variables still win.
208
+ - **`templates/automation-scaffold/playwright.config.ts`** - runner projects and auth are
209
+ generated from the lane table, replacing the commented-out
210
+ `chromium-user1` / `chromium-user2` block that sat outside the `projects` array and
211
+ could not have worked as written. `storageState` is derived through lane-lock rather
212
+ than hardcoded: a parallel hardcoded mapping caused real evidence corruption once, a run
213
+ recorded under the wrong account because the project was pinned to one lane while
214
+ `QA_LANE_ONLY` named another.
215
+ - **`global-setup.ts`** - `resolveAuthUsers` resolved lanes by substring
216
+ (`storageState.includes('user-2')`, which also matches `user-20`) and had no branch for
217
+ a third lane. It now matches the lane table exactly and reads credentials from each
218
+ lane's declared account prefix.
219
+ - **Agent behavior rules** - a BLOCKING lane-lock rule states the requirement only
220
+ (acquire before dispatching, release when done, dispatcher keeps the lane alive, queue
221
+ or reject at the cap, never force past it), agent-agnostic so each agent's adaptation
222
+ prescribes its own mechanism.
223
+ - **`qa-test-plan` / `qa-test-cases`** - skill content consolidated into the templates;
224
+ `priority-and-feasibility.md` merged into `plan-de-pruebas-template.md`.
225
+ - **Typographic characters** - em-dashes, en-dashes, arrows, smart quotes and ellipses
226
+ replaced with ASCII across `scripts/`, `skills/`, `templates/`, `docs/`, `examples/`,
227
+ `integrations/` and the PowerShell scripts. Substitution is context-sensitive: padded
228
+ prose dashes become ` - `, numeric and label ranges keep a tight hyphen, standalone
229
+ table cells become a bare `-`, and ASCII rules keep their original column width.
230
+ `CHANGELOG.md` keeps its historical occurrences deliberately - rewriting past entries to
231
+ satisfy a rule adopted later would falsify the log.
232
+
233
+ ### Fixed
234
+
235
+ - **`scripts/init.js`** - module specs are scaffolded under
236
+ `qa/01-specifications/{module}/{submodule}/`. init wrote them to `qa/{module}/`, leaving
237
+ `01-specifications/` permanently empty while scattering specs as siblings of the
238
+ numbered folders. The existing tests ran with no config, so the module loop iterated
239
+ zero times and no spec path was ever asserted.
240
+ - **`scripts/validate.js`** - check 2 walked all of `qa/`, treating every top-level
241
+ directory as a module, so it read `01-specifications/` as the module and demanded the 6
242
+ spec files one level above where they exist. The scan is now rooted at
243
+ `01-specifications/`, which also stops sibling folders such as `02-test-plans/` and
244
+ `memory/` from being mistaken for submodules; the `SKIP_DIRS` denylist is removed rather
245
+ than extended.
246
+ - **`scripts/validate.js`** - `--strict` looked for automation specs at
247
+ `e2e/{module}/{submodule}.spec.ts`, a path nothing creates. `init.js` writes to
248
+ `e2e/tests/{module}/` and `upgrade.js` documents the v1.6.0 migration that moved them
249
+ there, so `--strict` emitted a permanently false warning for every submodule in all
250
+ three live projects.
251
+ - **`scripts/validate.js`** - stopped requiring `05-test-plans`, a stale name from an
252
+ older convention; the current structure uses `05-test-execution` with test plans under
253
+ `02-test-plans`.
254
+ - **`templates/*/fixtures` and both scaffolds** - the four remaining `fill(password)` call
255
+ sites are listed under Security below.
256
+ - **`templates/agents/`** - the four Spanish-language agent templates shipped entirely
257
+ accent-stripped (144, 190, 88 and 52 Spanish markers, 0 to 2 accented characters between
258
+ them). This is functional, not cosmetic: page objects locate fields by visible label, so
259
+ a spec written as `Codigo` never matches a UI rendering the accented form. In one
260
+ project that cost 24 of 39 failing smoke tests and two wrong mitigations before the
261
+ cause was found.
262
+ - **`templates/defect-report.md`** - the defect ID prefix is a placeholder. The template
263
+ hardcoded `DEF-{{NNN}}` while `defectIdPattern` carried `DEF-{NUM}`, and real projects
264
+ use a project prefix such as `DEF-SIS-014`.
265
+ - **`templates/qa-framework.instructions.md` and `qa-framework.rules.md`** - referenced
266
+ `integrations.ado.enabled`, a config path that never existed. The real schema is
267
+ `integrations.azureDevOps.enabled`.
268
+ - **`skills/qa-spec-generation`** - pointed at `references/spec-file-formats.md`, which
269
+ only exists under `qa-module-analysis/references/`, so Stage 2 could not find the
270
+ template it is told to use. `skills/qa-automation` referenced a `pom-template.md` that
271
+ did not exist.
272
+ - **`qa-test-stabilization`** - the official suite is the only source of truth. A polling
273
+ diagnostic script saw a toast 4 of 4 times while the official suite saw none, 6 of 6
274
+ across two independent runs, and the defect had already been closed on that standalone
275
+ evidence. Standalone output is now diagnostic input, never a verdict.
276
+ - **`templates/*/.env.example`** - both files carried a UTF-8 BOM and three em-dashes. A
277
+ BOM in a `.env` is not cosmetic: some parsers read the first key as `QA_BASE_URL`
278
+ and silently fail to find it, and these files ship to every consuming project as the
279
+ template developers copy to `.env`. The character gate's extension list gains
280
+ `.example` and `.ps1`, both previous blind spots.
281
+ - **CI** - the test script used `test/**/*.test.js`, which needs globstar the runner's
282
+ shell does not enable, and the follow-up fix kept the pattern quoted so node received a
283
+ literal path. The validate step ran against this repository's own gitignored `qa/`
284
+ directory and failed with four missing-folder errors; it now scaffolds a throwaway
285
+ project and validates that, which exercises the contract that actually matters - what
286
+ `init.js` generates must pass `validate.js`.
287
+
288
+ ### Security
289
+
290
+ - **Password no longer reaches Playwright traces.** Both scaffolds typed the QA password
291
+ with `locator().fill(password)`, and the automation scaffold ships `trace` and `video`
292
+ set to `retain-on-failure`, so any failed CI run persisted an artifact containing the
293
+ plaintext password. All four call sites - `global-setup.ts` in both scaffolds,
294
+ `fixtures/base.ts` (TTL-aware reauth) and `fixtures/auth.ts` (`loginAs`) - now set the
295
+ value through `page.evaluate()`, the mechanism this package already documented as
296
+ "Pattern 7: Password Injection (Trace Safety)". The fixture sites carried more exposure
297
+ than `global-setup`, which runs once per run, since reauth runs for every test past the
298
+ TTL and `loginAs` for every test that logs in as a role. Bubbling `input` and `change`
299
+ events are dispatched after the assignment because a bare `.value` write does not notify
300
+ the Blazor/Radzen-style frameworks these scaffolds target, and an explicit visible
301
+ `waitFor` replaces the auto-wait `fill()` provided. Email `fill()` calls are left in
302
+ place: emails are not secret and aid debugging. There are no remaining
303
+ `fill(password)` call sites in `templates/`.
304
+ - **`.env` is no longer loaded wholesale** by the Playwright configs or the lane lock. See
305
+ Changed above.
306
+
307
+ ---
308
+
309
+ ## [1.11.3] - 2026-06-24
310
+
311
+ Reconstructed from `5507f1a..4fe32fb`. Release plumbing only.
312
+
313
+ ### Changed
314
+
315
+ - **`.github/workflows/publish.yml`** - `actions/checkout` and `actions/setup-node` move
316
+ from v4 to v6, and the publish job's Node version from 20 to 24.
317
+
318
+ ### Removed
319
+
320
+ - **`NODE_AUTH_TOKEN` / `secrets.NPM_TOKEN`** from the publish step, completing the move
321
+ to npm trusted publishing begun in 1.11.1: the job now authenticates with the
322
+ short-lived OIDC token the `id-token: write` permission grants, so no long-lived npm
323
+ token is stored in the repository.
324
+
325
+ No package content changed. Nothing reached consuming projects.
326
+
327
+ ---
328
+
329
+ ## [1.11.2] - 2026-06-24
330
+
331
+ Version bump only - a republish of 1.11.1 after a failed publish run. No source,
332
+ template, skill or script change; the diff is `package.json` and `package-lock.json`.
333
+
334
+ ---
335
+
336
+ ## [1.11.1] - 2026-06-24
337
+
338
+ Reconstructed from `3a29617..59f6fe0`. Release plumbing only.
339
+
340
+ ### Added
341
+
342
+ - **`.github/workflows/publish.yml`** - `id-token: write` permission on the publish job,
343
+ the prerequisite for npm trusted publishing. The long-lived token it replaces is dropped in
344
+ 1.11.3.
345
+
346
+ No package content changed. Nothing reached consuming projects.
347
+
348
+ ---
349
+
10
350
  ## [1.11.0] - 2026-06-24
11
351
 
12
352
  ### Added
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # qa-framework
2
2
 
3
3
  > Reusable, installable, agent-oriented QA framework for spec-driven automated testing.
4
- > Current version: **v1.12.0**
4
+ > Current version: **v1.13.0**
5
5
 
6
6
  ---
7
7
 
@@ -245,6 +245,46 @@ See [docs/skills-architecture.md](docs/skills-architecture.md) for the full desi
245
245
 
246
246
  ---
247
247
 
248
+ ## Claude Code Sprint Agents (optional)
249
+
250
+ When `integrations.azureDevOps.sprintCycle.enabled` is `true`, `init` (only if missing) and
251
+ `upgrade` (always, framework-owned) generate six Claude Code subagents under `.claude/agents/`:
252
+
253
+ | Agent | What it does |
254
+ |---|---|
255
+ | `qa-analisis` | Sprint analysis document: test universe, automation feasibility, P0-P3 prioritization, traceability matrix |
256
+ | `qa-plan` | Sprint test plan: executive summary plus a trace-linked test case table that fits the manual-testing timebox |
257
+ | `qa-asesoria` | Chat-only QA advisory (no `Write`/`Bash`) |
258
+ | `qa-reporte-ejecucion` | Exports the ADO test plan results (via the `ado-qa` `getADO.ps1` script), versions the raw export by date and writes `Reporte-de-Ejecucion-de-Pruebas-<project>-Sprint-<N>.md` |
259
+ | `qa-informe-resultados` | Narrative sprint closing report (metrics, failures, risks, QA verdict) built from the Reporte above |
260
+ | `qa-investigador` | Investigates one concrete root cause against the real environment and returns a verdict with evidence; drives a browser over Playwright MCP when lanes provide one |
261
+
262
+ Config keys read by the agents (all optional; defaults shown):
263
+
264
+ | Key | Default | Used for |
265
+ |---|---|---|
266
+ | `integrations.azureDevOps.sprintCycle.approvalThresholdPercent` | `85` | Success rate over executed cases required for "Aprobado con observaciones" |
267
+ | `integrations.azureDevOps.sprintCycle.sprintDurationDays` / `manualTestingTimeboxDays` | `8` / `2` | Sprint planning agents |
268
+ | `integrations.azureDevOps.sprintCycle.dateFormat` / `timezone` | `dd-mm-aaaa` / `America/Santiago` | Dates in generated documents |
269
+ | `conventions.decimalSeparator` | derived: `,` for `es*` locales or no locale, `.` otherwise | Percentages in reports |
270
+ | `conventions.defectIdPrefix` | derived from `project.name` (uppercased) | Local defect candidate ids `DEF-<prefix>-NNN` |
271
+ | `conventions.testCaseIdPattern` | `TC-{MODULE}-{SUBMODULE}-{NUM}` | Local ids kept out of ADO-facing documents |
272
+ | `project.qaBaseUrl` / `loginPath` / `techStack` | "(no configurado: ...)" | Environment context for `qa-investigador` |
273
+
274
+ `qa-investigador` and `parallelLanes`:
275
+
276
+ - **0 MCP lanes** (no `parallelLanes`, `enabled: false`, or no lane with `mcpNamespace`): tools are
277
+ `Read, Write, Edit, Grep, Glob, Bash` and the agent investigates through code, config, logs and the
278
+ CLI runner, reporting browser-only hypotheses as blocked.
279
+ - **1 or N MCP lanes**: every lane with an `mcpNamespace` (e.g. `mcp__playwright-qauser__`) adds its
280
+ 15 Playwright MCP browser tools, in config order, plus a lane table and the mandatory
281
+ `lane-lock.js heartbeat` protocol. CLI-only lanes are skipped.
282
+ - An invalid lane block or an `mcpNamespace` not matching `^mcp__[A-Za-z0-9_-]+__$` fails
283
+ `qa-investigador` only: the other five agents are still written and the command exits 1.
284
+ `qa-framework validate` reports the same problems.
285
+
286
+ ---
287
+
248
288
  ## CLI Reference
249
289
 
250
290
  ```
@@ -161,8 +161,9 @@ Use environment variables:
161
161
  #### Optional: ANALISIS/PLAN agent mode (Claude Code only)
162
162
 
163
163
  Gated by `integrations.azureDevOps.sprintCycle.enabled`. When `true`, `init`/`upgrade`
164
- generate four Claude Code subagents (`.claude/agents/qa-analisis.md`, `qa-plan.md`,
165
- `qa-asesoria.md`, `qa-informe-resultados.md`) for a sprint-centric manual testing
164
+ generate six Claude Code subagents (`.claude/agents/qa-analisis.md`, `qa-plan.md`,
165
+ `qa-asesoria.md`, `qa-reporte-ejecucion.md`, `qa-informe-resultados.md`,
166
+ `qa-investigador.md`) for a sprint-centric manual testing
166
167
  workflow parallel to the 6-stage pipeline. See
167
168
  [docs/usage-with-agent.md](usage-with-agent.md) for details.
168
169
 
@@ -176,7 +177,8 @@ workflow parallel to the 6-stage pipeline. See
176
177
  "sprintDurationDays": 8,
177
178
  "manualTestingTimeboxDays": 2,
178
179
  "dateFormat": "dd-mm-aaaa",
179
- "timezone": "America/Santiago"
180
+ "timezone": "America/Santiago",
181
+ "approvalThresholdPercent": 85
180
182
  }
181
183
  }
182
184
  }
@@ -184,7 +186,12 @@ workflow parallel to the 6-stage pipeline. See
184
186
  ```
185
187
 
186
188
  All `sprintCycle` fields are optional; omitted fields fall back to neutral framework
187
- defaults (8-day sprint, 2-day manual testing timebox, `dd-mm-aaaa`, `America/Santiago`).
189
+ defaults (8-day sprint, 2-day manual testing timebox, `dd-mm-aaaa`, `America/Santiago`,
190
+ 85% approval threshold over executed cases). Optional overrides under `conventions`:
191
+ `decimalSeparator` (`","` or `"."`, otherwise derived from the locale) and
192
+ `defectIdPrefix` (e.g. `"ACME"`, otherwise derived from `project.name`).
193
+ `qa-investigador` gets Playwright MCP browser tools only for `parallelLanes.lanes[]`
194
+ entries with an `mcpNamespace` (pattern `^mcp__[A-Za-z0-9_-]+__$`).
188
195
  - `ADO_PLAN_ID` - Test Plan ID (can also go in `module-registry.json`)
189
196
  - `ADO_SUITE_ID` - Suite ID
190
197
 
@@ -125,26 +125,31 @@ ad-hoc QA advisory chat, and a sprint closing results report. This mode is:
125
125
 
126
126
  - **Optional** - off by default.
127
127
  - **Gated** by `integrations.azureDevOps.sprintCycle.enabled` in
128
- `qa/qa-framework.config.json` (it requires Azure DevOps enabled, since three of the
129
- four agents depend on ADO work items or an ADO-generated execution report).
128
+ `qa/qa-framework.config.json` (it requires Azure DevOps enabled, since most of the
129
+ agents depend on ADO work items or an ADO-generated execution report).
130
130
  - **Claude Code only, by design, not by omission.** Each agent is a Claude Code subagent
131
131
  with its own `tools`/`model` frontmatter (e.g. `qa-asesoria` intentionally has no
132
132
  `Write`/`Bash`). GitHub Copilot has no equivalent primitive, so no `.github/` artifact
133
133
  is generated for this mode.
134
134
 
135
- When enabled, `init`/`upgrade` generate four subagents under `.claude/agents/`:
135
+ When enabled, `init`/`upgrade` generate six subagents under `.claude/agents/`:
136
136
 
137
137
  | Agent | Purpose |
138
138
  |---|---|
139
139
  | `qa-analisis.md` | Analysis document: test universe, automation feasibility, P0-P3 prioritization, excluded universe, traceability matrix |
140
140
  | `qa-plan.md` | Default mode: executive summary + a trace-linked test case table, executable within the configured manual-testing timebox |
141
141
  | `qa-asesoria.md` | Chat-only QA advisory (no `Write`/`Bash`) - answers point questions, redirects to `qa-plan`/`qa-analisis` when a full document is actually needed |
142
- | `qa-informe-resultados.md` | Narrative sprint closing report built from an execution report already produced by the `qa-ado-integration` skill; updates incrementally, never overwrites prior sections |
143
-
144
- Sprint duration, manual-testing timebox, date format, and timezone are parameterized
145
- from `integrations.azureDevOps.sprintCycle` (with neutral defaults - 8-day sprint,
146
- 2-day timebox, `dd-mm-aaaa`, `America/Santiago` - when a field is omitted). See
147
- [docs/installation.md](installation.md) for the config shape.
142
+ | `qa-reporte-ejecucion.md` | Exports the ADO test plan results, versions the raw export under `ado-export-<YYYY-MM-DD>/` and writes the sprint `Reporte-de-Ejecucion-de-Pruebas-<project>-Sprint-<N>.md` (summary + promoted findings) |
143
+ | `qa-informe-resultados.md` | Narrative sprint closing report built from the Reporte produced by `qa-reporte-ejecucion`; updates incrementally, never overwrites prior sections |
144
+ | `qa-investigador.md` | Investigates one concrete root cause against the real environment and returns a verdict with evidence. Gets Playwright MCP browser tools for every `parallelLanes` lane with an `mcpNamespace`; with no such lane it works without a browser |
145
+
146
+ Sprint duration, manual-testing timebox, date format, timezone and the approval threshold
147
+ (`approvalThresholdPercent`, default 85) are parameterized from
148
+ `integrations.azureDevOps.sprintCycle` (with neutral defaults - 8-day sprint, 2-day
149
+ timebox, `dd-mm-aaaa`, `America/Santiago` - when a field is omitted). The optional
150
+ `conventions.decimalSeparator` and `conventions.defectIdPrefix` override values otherwise
151
+ derived from the locale and the project name. See [docs/installation.md](installation.md)
152
+ for the config shape.
148
153
 
149
154
  ---
150
155
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keber/qa-framework",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "Reusable spec-driven QA framework for IDE-agent-assisted automated testing. Installable as an npm package. Provides structure, templates, agent instructions, and optional integrations for Playwright and Azure DevOps.",
5
5
  "keywords": [
6
6
  "qa",
@@ -25,7 +25,7 @@
25
25
  "check-accents": "node scripts/check-spanish-accents.js",
26
26
  "check-chars": "node scripts/check-forbidden-chars.js",
27
27
  "test": "node --test test/*.test.js",
28
- "version": "node scripts/sync-version.js && git add README.md .github/copilot-instructions.md"
28
+ "version": "node scripts/sync-version.js && git add README.md .github/copilot-instructions.md qa-framework.config.json"
29
29
  },
30
30
  "files": [
31
31
  "agent-instructions/",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "./qa-framework.config.schema.json",
3
- "frameworkVersion": "1.10.0",
3
+ "frameworkVersion": "1.13.0",
4
4
 
5
5
  "project": {
6
6
  "name": "{{PROJECT_NAME}}",
@@ -89,7 +89,8 @@
89
89
  "sprintDurationDays": 8,
90
90
  "manualTestingTimeboxDays": 2,
91
91
  "dateFormat": "dd-mm-aaaa",
92
- "timezone": "America/Santiago"
92
+ "timezone": "America/Santiago",
93
+ "approvalThresholdPercent": 85
93
94
  }
94
95
  }
95
96
  },
package/scripts/init.js CHANGED
@@ -16,7 +16,7 @@ const fs = require('fs');
16
16
  const path = require('path');
17
17
 
18
18
  const { buildCommandContent, discoverSkillNames, commandFileName } = require('./lib/claude-commands');
19
- const { AGENT_NAMES, isSprintCycleEnabled, buildAgentContent, agentFileName } = require('./lib/claude-agents');
19
+ const { AGENT_NAMES, isSprintCycleEnabled, renderAllAgents } = require('./lib/claude-agents');
20
20
 
21
21
  // --- Parse args ---
22
22
  const args = process.argv.slice(2);
@@ -195,13 +195,26 @@ const SPEC_FILES = [
195
195
 
196
196
  const templateDir = path.resolve(__dirname, '..', 'templates', 'specification');
197
197
 
198
+ // Spec folders carry the module-/submodule- prefix; automation folders and TC ids
199
+ // use the bare key. See scripts/lib/module-keys.js for why the two differ.
200
+ const {
201
+ moduleSpecDirName,
202
+ submoduleSpecDirName,
203
+ moduleBareName,
204
+ submoduleBareName,
205
+ } = require('./lib/module-keys');
206
+
198
207
  for (const mod of modules) {
199
208
  const moduleKey = mod.key ?? mod.name.toLowerCase().replace(/\s+/g, '-');
209
+ const moduleDirName = moduleSpecDirName(moduleKey);
210
+ const moduleName = moduleBareName(moduleKey);
200
211
  const submodules = mod.submodules ?? [];
201
212
 
202
213
  for (const sub of submodules) {
203
214
  const subKey = sub.key ?? sub.name.toLowerCase().replace(/\s+/g, '-');
204
- const subDir = path.join(qaRoot, '01-specifications', moduleKey, subKey);
215
+ const subDirName = submoduleSpecDirName(subKey);
216
+ const subName = submoduleBareName(subKey);
217
+ const subDir = path.join(qaRoot, '01-specifications', moduleDirName, subDirName);
205
218
  fs.mkdirSync(subDir, { recursive: true });
206
219
 
207
220
  for (const specFile of SPEC_FILES) {
@@ -213,8 +226,8 @@ for (const mod of modules) {
213
226
  content = content
214
227
  .replace(/\{\{MODULE_NAME\}\}/g, mod.name)
215
228
  .replace(/\{\{SUBMODULE_NAME\}\}/g, sub.name)
216
- .replace(/\{\{MODULE\}\}/g, moduleKey.toUpperCase())
217
- .replace(/\{\{SUB\}\}/g, subKey.toUpperCase());
229
+ .replace(/\{\{MODULE\}\}/g, moduleName.toUpperCase())
230
+ .replace(/\{\{SUB\}\}/g, subName.toUpperCase());
218
231
  fs.writeFileSync(dest, content, 'utf8');
219
232
  } else {
220
233
  fs.writeFileSync(dest, `# ${specFile}\n\n> Auto-generated placeholder\n`, 'utf8');
@@ -226,11 +239,11 @@ for (const mod of modules) {
226
239
  }
227
240
 
228
241
  // Create automation spec stub in e2e/tests/{module}/
229
- const e2eDir = path.join(qaRoot, '07-automation', 'e2e', 'tests', moduleKey);
242
+ const e2eDir = path.join(qaRoot, '07-automation', 'e2e', 'tests', moduleName);
230
243
  fs.mkdirSync(e2eDir, { recursive: true });
231
- const specTs = path.join(e2eDir, `${subKey}.spec.ts`);
244
+ const specTs = path.join(e2eDir, `${subName}.spec.ts`);
232
245
  if (!fs.existsSync(specTs)) {
233
- fs.writeFileSync(specTs, specScaffold(mod.name, sub.name, moduleKey, subKey), 'utf8');
246
+ fs.writeFileSync(specTs, specScaffold(mod.name, sub.name, moduleName, subName), 'utf8');
234
247
  console.log(` [created] ${path.relative(cwd, specTs)}`);
235
248
  }
236
249
  }
@@ -347,12 +360,19 @@ writeIfMissing(claudeRulesPath, claudeRulesContent);
347
360
  // --- .claude/agents/qa-*.md - optional ANALISIS/PLAN sprint-cycle mode (Claude Code only) ---
348
361
  // Gated by integrations.azureDevOps.sprintCycle.enabled. No .github/ equivalent: Claude
349
362
  // Code subagents with per-agent tools/model frontmatter have no Copilot counterpart.
363
+ // Each agent is rendered independently: an invalid lane config fails qa-investigador
364
+ // only, the other agents are still written, and init exits 1 at the end.
365
+ const agentFailures = [];
350
366
  if (isSprintCycleEnabled(config)) {
351
367
  const templatesDir = path.resolve(__dirname, '..', 'templates');
352
368
  const claudeAgentsDest = path.join(cwd, '.claude', 'agents');
353
369
  fs.mkdirSync(claudeAgentsDest, { recursive: true });
354
- for (const agentName of AGENT_NAMES) {
355
- writeIfMissing(path.join(claudeAgentsDest, agentFileName(agentName)), buildAgentContent(agentName, templatesDir, config));
370
+ for (const agent of renderAllAgents(templatesDir, config)) {
371
+ if (agent.error) {
372
+ agentFailures.push(agent);
373
+ continue;
374
+ }
375
+ writeIfMissing(path.join(claudeAgentsDest, agent.fileName), agent.content);
356
376
  }
357
377
  }
358
378
 
@@ -414,7 +434,11 @@ console.log(' .github/instructions/ qa-framework.instructions.md');
414
434
  console.log(' .claude/commands/ qa-*.md (Claude Code slash commands)');
415
435
  console.log(' .claude/rules/ qa-framework.md (Claude Code always-loaded rules)');
416
436
  if (isSprintCycleEnabled(config)) {
417
- console.log(' .claude/agents/ qa-analisis/qa-plan/qa-asesoria/qa-informe-resultados.md (ANALISIS/PLAN mode)');
437
+ const failedAgents = new Set(agentFailures.map((a) => a.agentName));
438
+ const generatedAgents = AGENT_NAMES.filter((name) => !failedAgents.has(name));
439
+ if (generatedAgents.length) {
440
+ console.log(` .claude/agents/ ${generatedAgents.join('/')}.md (ANALISIS/PLAN mode)`);
441
+ }
418
442
  }
419
443
  console.log('');
420
444
  console.log(' Optional integrations:');
@@ -438,6 +462,23 @@ console.log('');
438
462
  console.log(' Next: edit qa/qa-framework.config.json, then read qa/AGENT-NEXT-STEPS.md');
439
463
  console.log('');
440
464
 
465
+ if (agentFailures.length) {
466
+ if (skipIfExists) {
467
+ // Postinstall mode: a non-zero exit would make `npm install` of this package fail,
468
+ // which blocks the user from ever reaching the fix. Report loudly, exit 0.
469
+ for (const { agentName, fileName, error } of agentFailures) {
470
+ console.error(`[qa-framework/init] WARNING: agent ${agentName} (.claude/agents/${fileName}) was not generated. ${error.message}`);
471
+ }
472
+ console.error('[qa-framework/init] WARNING: fix qa-framework.config.json (run "npx qa-framework validate"), then run "npx qa-framework upgrade" to generate the missing agent.');
473
+ } else {
474
+ for (const { fileName, error } of agentFailures) {
475
+ console.error(`[qa-framework/init] ERROR: .claude/agents/${fileName} was not generated. ${error.message}`);
476
+ }
477
+ console.error('[qa-framework/init] Fix qa-framework.config.json (run "qa-framework validate") and run init or upgrade again.');
478
+ process.exitCode = 1;
479
+ }
480
+ }
481
+
441
482
  // -----------------------------------------------------------------------
442
483
  // Helpers
443
484
  // -----------------------------------------------------------------------