diff --git a/.claude/rules/qa-framework-dev.md b/.claude/rules/qa-framework-dev.md
new file mode 100644
index 0000000..b21d16d
--- /dev/null
+++ b/.claude/rules/qa-framework-dev.md
@@ -0,0 +1 @@
+@.github/copilot-instructions.md
diff --git a/.claude/settings.json b/.claude/settings.json
new file mode 100644
index 0000000..47225c6
--- /dev/null
+++ b/.claude/settings.json
@@ -0,0 +1,7 @@
+{
+ "attribution": {
+ "commit": "",
+ "pr":"",
+ "sessionUrl": false
+ }
+}
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 96c5add..7efeccf 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- node-version: ['18', '20']
+ node-version: ['20', '22', '24']
steps:
- uses: actions/checkout@v6
@@ -22,4 +22,15 @@ jobs:
- run: npm test
- - run: npm run validate
+ # validate checks a scaffolded qa/ tree. This repo's own qa/ is gitignored,
+ # so it never exists on the runner - validating it here checked nothing.
+ # Scaffold a throwaway project instead: that exercises the real contract,
+ # which is that what init.js generates passes validate.js.
+ - name: Scaffold a project and validate it
+ run: |
+ set -e
+ scaffold="$(mktemp -d)"
+ cd "$scaffold"
+ node "$GITHUB_WORKSPACE/scripts/init.js"
+ node "$GITHUB_WORKSPACE/scripts/validate.js"
+ node "$GITHUB_WORKSPACE/scripts/validate.js" --strict
diff --git a/MIGRATION-NOTES.md b/MIGRATION-NOTES.md
index 569f017..bc0fa2c 100644
--- a/MIGRATION-NOTES.md
+++ b/MIGRATION-NOTES.md
@@ -12,7 +12,7 @@ each pattern to the decoupled package approach.
---
-## Pattern A — Repo A style (redacted-repo-web)
+## Pattern A - Repo A style (redacted-repo-web)
### Characteristics
@@ -61,7 +61,7 @@ each pattern to the decoupled package approach.
4. **Move existing spec files** (no structural changes needed):
```
- qa/07-automation/e2e/ ← keep your existing .spec.ts files here
+ qa/07-automation/e2e/ <- keep your existing .spec.ts files here
```
5. **Add missing standard folders**:
@@ -70,7 +70,7 @@ each pattern to the decoupled package approach.
```
Copy the standards templates:
- `node_modules/keber/qa-framework/templates/defect-report.md`
- → `qa/00-standards/bug-report-template.md`
+ -> `qa/00-standards/bug-report-template.md`
6. **Update `package.json` in `qa/07-automation/`**:
Replace the existing `gmoindustrial-qa-e2e` package name with your project name.
@@ -89,7 +89,7 @@ each pattern to the decoupled package approach.
---
-## Pattern B — Repo B style (redacted-repo)
+## Pattern B - Repo B style (redacted-repo)
### Characteristics
@@ -97,7 +97,7 @@ each pattern to the decoupled package approach.
- Has `00-standards/` with naming-conventions, bug-template, TC-template, test-data-guidelines
- Has `08-azure-integration/` with playwright-azure-reporter, inject-ado-ids.ps1, module-registry.json
- Multi-module structure: 4 modules × 17+ submodules
-- Full ADO integration: Plans 22304/22794/22875, WI IDs 22957–23034
+- Full ADO integration: Plans 22304/22794/22875, WI IDs 22957-23034
- Login: email-based auth
### Migration steps
@@ -133,15 +133,15 @@ each pattern to the decoupled package approach.
}
```
-3. **Keep existing `00-standards/` files** — they are compliant with the framework.
+3. **Keep existing `00-standards/` files** - they are compliant with the framework.
The framework's `templates/defect-report.md` is a generalization of the existing
bug-report template; no changes required.
4. **Keep existing `08-azure-integration/` files**:
- - `module-registry.json` — compatible as-is
+ - `module-registry.json` - compatible as-is
- Replace `inject-ado-ids.ps1` with the generalized version from
`node_modules/keber/qa-framework/integrations/ado-powershell/scripts/inject-ado-ids.ps1`
- (optional — existing script continues to work)
+ (optional - existing script continues to work)
5. **Verify `.gitignore`** contains:
```
@@ -164,10 +164,10 @@ each pattern to the decoupled package approach.
- **TC IDs**: The framework uses `[TC-MODULE-SUB-NNN]` format. Existing TCs with different
formats (e.g., plain numbers like `[TC-001]`) can be migrated by renaming at the next
spec refresh cycle; no immediate change required.
-- **ADO WI IDs**: Already injected IDs (`[22957]` prefixes) are compatible — the reporter
+- **ADO WI IDs**: Already injected IDs (`[22957]` prefixes) are compatible - the reporter
reads the numeric prefix regardless of what follows.
- **storageState files**: Existing `.auth/*.json` files are compatible with the scaffold's
- `global-setup.ts` — no migration needed.
+ `global-setup.ts` - no migration needed.
- **Package name**: The automation sub-package (`gmoindustrial-qa-e2e` or similar) is a
private local package; renaming is optional cosmetic change.
@@ -205,3 +205,56 @@ upgradeable without touching the project's custom Copilot instructions.
The detection marker is the heading `# QA Framework Instructions`, which is consistent
across all previous versions.
+
+---
+
+## Native Claude Code support (no manual per-project work needed)
+
+Previously, projects that used Claude Code instead of (or alongside) GitHub Copilot had
+to hand-build their own bridge to the framework's skills and instructions - there was no
+generated artifact for Claude Code at all. Two real consumer projects independently built
+two incompatible solutions to the same problem (a full skill mirror with a manual sync
+script in one case, hand-written thin command wrappers in the other).
+
+`init` and `upgrade` now generate Claude Code artifacts natively, in parallel with the
+Copilot ones, with no agent detection required:
+
+- `.claude/commands/qa-{name}.md` - one thin wrapper per skill, generated dynamically from
+ `.github/skills/`. Each wrapper points back at the corresponding `SKILL.md` as the single
+ source of truth (`Read the full skill at .github/skills/qa-{name}/SKILL.md FIRST...`).
+ Zero duplication, zero drift.
+- `.claude/rules/qa-framework.md` - the same 11 agent behavior rules and pipeline table as
+ `.github/instructions/qa-framework.instructions.md`, with skill references expressed as
+ `/qa-{name}` slash commands. No frontmatter, so it loads unconditionally (equivalent to
+ `applyTo: '**'`).
+
+Both files are framework-owned and refreshed safely by `npx qa-framework upgrade`, the
+same way the Copilot artifacts are. Projects that built a manual bridge to Claude Code
+before this version can retire it (custom sync scripts, hand-copied skill mirrors, or
+hand-translated `CLAUDE.md`/`.claude/rules/` content) and rely on the generated artifacts
+instead.
+
+---
+
+## Optional ANALISIS/PLAN sprint-cycle mode (Claude Code only)
+
+Previously, a project that wanted a sprint-centric manual testing workflow parallel to
+the 6-stage pipeline (analysis document, ADO-trace-linked test plan, chat-only QA
+advisory, sprint closing results report) had to hand-build its own Claude Code
+subagents - one real consumer project did exactly that, with the sprint duration,
+manual-testing timebox, date format, and locale all hardcoded to its own conventions.
+
+`init` and `upgrade` now generate this mode natively as four generic, parameterized
+subagents under `.claude/agents/` (`qa-analisis.md`, `qa-plan.md`, `qa-asesoria.md`,
+`qa-informe-resultados.md`), gated by `integrations.azureDevOps.sprintCycle.enabled` in
+`qa-framework.config.json`. It is off by default, requires Azure DevOps integration to
+be meaningful (three of the four agents consume ADO work items or an ADO execution
+report), and is intentionally Claude-only - these are Claude Code subagents with
+per-agent `tools`/`model` frontmatter, a primitive GitHub Copilot has no equivalent for.
+
+Projects that hand-built this mode before this version can retire their local copy and
+adopt the generated one, moving their project-specific sprint duration, manual-testing
+timebox, date format, and timezone into `integrations.azureDevOps.sprintCycle` in
+`qa-framework.config.json`. See [docs/usage-with-agent.md](docs/usage-with-agent.md) and
+[docs/installation.md](docs/installation.md) for the full config shape and generated
+file list.
diff --git a/README.md b/README.md
index feab331..94f31c5 100644
--- a/README.md
+++ b/README.md
@@ -176,7 +176,7 @@ qa/
├── {module}/{submodule}/ <- 6-file spec sets created per module/submodule in config
├── 02-test-plans/ <- Test plans (automated + manual)
├── 03-test-cases/ <- TC-*.md step-by-step docs (manual track; optional if fully automated)
-├── 04-test-data/ <- Shared test data factories/seeders (optional — per-module data lives in 01-specs)
+├── 04-test-data/ <- Shared test data factories/seeders (optional - per-module data lives in 01-specs)
├── 05-test-execution/ <- Execution reports and results
├── 06-defects/open|resolved/ <- Defect tracking
├── 07-automation/ <- Playwright automation code and config
diff --git a/docs/architecture.md b/docs/architecture.md
index bf4178c..6320e5a 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -9,11 +9,11 @@
## Design Goals
-1. **Decoupled core** — the framework works without Playwright, without Azure DevOps, without any specific CI/CD system
-2. **Layered optionality** — features are added as explicit opt-in integrations, not baked into the core
-3. **Agent-first design** — every convention exists so that an IDE agent can navigate and produce artifacts predictably
-4. **Spec-before-automation** — the specification layer is always the source of truth; automation references specs, never the reverse
-5. **Parameterization over hardcoding** — project-specific values live in `qa-framework.config.json`, not in framework files
+1. **Decoupled core** - the framework works without Playwright, without Azure DevOps, without any specific CI/CD system
+2. **Layered optionality** - features are added as explicit opt-in integrations, not baked into the core
+3. **Agent-first design** - every convention exists so that an IDE agent can navigate and produce artifacts predictably
+4. **Spec-before-automation** - the specification layer is always the source of truth; automation references specs, never the reverse
+5. **Parameterization over hardcoding** - project-specific values live in `qa-framework.config.json`, not in framework files
---
@@ -86,6 +86,8 @@
│ │
│ .github/instructions/qa-framework.instructions.md <- generated (framework) │
│ .github/skills/ <- copied from package (framework-owned) │
+│ .claude/rules/qa-framework.md <- generated (framework, Claude Code) │
+│ .claude/commands/qa-*.md <- generated (framework, Claude Code) │
└─────────────────────────────────────────────────────────────────────────────┘
```
@@ -98,12 +100,12 @@ Module analysis (agent)
│
▼
01-specifications/module-X/submodule-Y/
- ├── 00-inventory.md ← what exists in the UI
- ├── 01-business-rules.md ← RN-* identifiers
- ├── 02-workflows.md ← FL-* flowcharts
+ ├── 00-inventory.md <- what exists in the UI
+ ├── 01-business-rules.md <- RN-* identifiers
+ ├── 02-workflows.md <- FL-* flowcharts
├── 03-roles-permissions.md
├── 04-test-data.md
- └── 05-test-scenarios.md ← TC-* identifiers ──────────────────────┐
+ └── 05-test-scenarios.md <- TC-* identifiers ──────────────────────┐
│
│ │
▼ ▼
@@ -120,8 +122,8 @@ Module analysis (agent)
│
▼
[ADO enabled?]
- YES → playwright-azure-reporter syncs results to ADO Test Plan
- NO → 05-test-execution/automated/{date}.md (local report)
+ YES -> playwright-azure-reporter syncs results to ADO Test Plan
+ NO -> 05-test-execution/automated/{date}.md (local report)
│
▼
06-defects/ (if test.skip for known bug)
@@ -132,18 +134,20 @@ Module analysis (agent)
## Layer Definitions
-### Layer 1 — Framework Core (mandatory)
+### Layer 1 - Framework Core (mandatory)
Installed always. Contains:
- `qa/` directory skeleton (10 folders)
-- `.github/skills/` — 8 agent skill sets (3-layer model: SKILL.md + references/)
-- `00-standards/` — naming conventions, templates
+- `.github/skills/` - 8 agent skill sets (3-layer model: SKILL.md + references/)
+- `00-standards/` - naming conventions, templates
- `QA-STRUCTURE-GUIDE.md`
-- `.github/copilot-instructions.md` — generated pipeline sequencer
+- `.github/instructions/qa-framework.instructions.md` - generated pipeline sequencer (Copilot)
+- `.claude/rules/qa-framework.md` - generated pipeline sequencer (Claude Code, unconditional load)
+- `.claude/commands/qa-*.md` - generated thin wrappers, one per skill (Claude Code)
- `qa-framework.config.json` schema
-### Layer 2 — Playwright Integration (opt-in)
+### Layer 2 - Playwright Integration (opt-in)
Installed always (scaffold is always created by `init`):
@@ -156,7 +160,7 @@ Installed always (scaffold is always created by `init`):
- `qa/07-automation/integration/README.md` (placeholder)
- `qa/07-automation/load/README.md` (placeholder)
-### Layer 3 — Azure DevOps Integration (opt-in)
+### Layer 3 - Azure DevOps Integration (opt-in)
Installed when `integrations.azureDevOps.enabled = true`:
@@ -173,11 +177,11 @@ Installed when `integrations.azureDevOps.enabled = true`:
```
qa-framework.config.json (project-level, committed to repo)
│
- ├── project.* → Display values, URLs (non-secret)
- ├── modules[] → Module codes, paths, ADO IDs
- ├── conventions.* → Naming patterns, TC ID format
- ├── testUsers[] → Role→envVar mapping (NOT credentials)
- └── integrations.* → Feature flags + integration config
+ ├── project.* -> Display values, URLs (non-secret)
+ ├── modules[] -> Module codes, paths, ADO IDs
+ ├── conventions.* -> Naming patterns, TC ID format
+ ├── testUsers[] -> Role->envVar mapping (NOT credentials)
+ └── integrations.* -> Feature flags + integration config
│
└── credentials come from:
.env (local, gitignored)
@@ -227,6 +231,6 @@ Full rules: [docs/folder-structure-guide.md](folder-structure-guide.md)
1. The primary test runner is Playwright. Other runners (Jest, Cypress) are not excluded but are not provided adapters in v1.0.
2. The target application runs in a browser. Back-end API-only testing is not the primary use case of this framework (though API testing can be added to `07-automation/` as needed).
-3. The IDE agent is GitHub Copilot or equivalent. The instructions are written in Markdown and are IDE-agnostic.
+3. The IDE agent is GitHub Copilot, Claude Code, or equivalent. The instructions are written in Markdown and are IDE-agnostic; `init`/`upgrade` generate native artifacts for both Copilot (`.github/instructions/`) and Claude Code (`.claude/rules/`, `.claude/commands/`) in parallel, without detecting which agent the consumer uses.
4. The project uses Git. The `qa/` directory lives inside the same repository as the application code (monorepo-friendly).
5. Credentials are always managed via environment variables. There is no fallback to hardcoded credentials in any framework file.
diff --git a/docs/decisions/lane-heartbeat-caller.md b/docs/decisions/lane-heartbeat-caller.md
new file mode 100644
index 0000000..39f22b7
--- /dev/null
+++ b/docs/decisions/lane-heartbeat-caller.md
@@ -0,0 +1,86 @@
+# Decision: the dispatcher calls the lane heartbeat, not the executor
+
+**Status**: accepted, not implemented
+**Date**: 2026-09-08
+**Scope**: the N-lane work still pending upstream (FRAMEWORK-FIXES v3 items 1-4, v5 items 1, 2, 4)
+
+---
+
+## Context
+
+Parallel QA lanes exist to work around a constraint of the applications under test:
+one account cannot hold two sessions at once, so a second login silently invalidates
+the first. A lane lock assigns each concurrent worker its own account.
+
+A lock is only useful if a held lane stays held. The reference implementation
+(`lane-lock.js` in the Sispro Exportadora project) frees a lane when it looks stale,
+using a composite predicate: the acquisition is older than 20 minutes AND either no
+heartbeat was ever sent OR the last heartbeat is itself older than 20 minutes. A
+recent heartbeat proves the holder is alive, so the lane is never swept.
+
+That predicate is only as good as whoever calls `heartbeat`.
+
+## The incident that forced the decision
+
+On 2026-08-22 a delegated sub-agent spent over 20 minutes on a legitimate live
+investigation - genuine environment congestion, 80 to 90 concurrent browser and node
+processes - and never called `heartbeat`. The sweep did exactly what it was written
+to do and freed the lane while the sub-agent was still driving that account: the
+precise scenario the heartbeat exists to prevent.
+
+No cross-session collision followed, but only because a person noticed the lane was
+free with no matching completion report, paused the sub-agent, confirmed by explicit
+reply that nothing had run against the browser after the sweep, and re-reserved the
+lane. The protection failed by design and was caught by attentiveness.
+
+## Decision
+
+**The dispatcher calls `heartbeat` from its own wait loop.** The executor is not
+required to call it and must not be relied upon to.
+
+## Why
+
+The executor option fails for a structural reason, not a discipline one. A delegated
+agent has no reason to discover `heartbeat` unless the dispatching prompt tells it,
+every single time. That is the same failure this project has now recorded four times
+over: a known-issues record that existed and was not consulted, an orthography rule
+that was asserted rather than measured, and this heartbeat. A mechanism whose
+protection depends on correctly instructing an uninstructed party is not a control.
+
+The dispatcher, by contrast, already knows whether the task is still running - it can
+query task state directly. The executor cannot see how long it has been between its
+own tool calls. Putting the call where the knowledge already is removes the
+instruction step entirely, which is where option 1 actually broke.
+
+The cost is real and accepted: the dispatcher's wait loop becomes more complex, and
+the concrete implementation is agent-specific rather than framework-generic.
+
+## Layering
+
+This follows the split established in FRAMEWORK-FIXES v3 section 5.
+
+The agent-agnostic framework states the requirement: a lane must be acquired through
+the lock before any worker is dispatched against it, released when the work ends, and
+kept alive by the dispatcher while the work is in flight. Each agent's adaptation
+prescribes the mechanism. For Claude Code that means a `/loop` wrapper with a
+`ScheduleWakeup` fallback and a non-blocking task-state check - primitives that have
+no equivalent in other agents, and so must not be written into
+`templates/qa-framework.instructions.md`.
+
+## Consequences
+
+- The N-lane scaffold ships `heartbeat` together with the rule naming its caller.
+ Shipping the sweep without that answer reproduces the 2026-08-22 incident in every
+ project that adopts it.
+- A lane held far longer than the application's session TTL is a signal to verify,
+ not a defect to ignore. Each project's agent-specific instructions must define how
+ it verifies a lock is still legitimately held and how it clears a stale one.
+- Neither option was implemented in the originating project, so there is no verified
+ reference implementation of the dispatcher-side loop to lift. It has to be written.
+
+## Sources
+
+- `temp/FRAMEWORK-FIXES-qa-framework-v5.md` item 5, which framed the fork and
+ recommended this side.
+- `temp/FRAMEWORK-FIXES-qa-framework-v3.md` section 5, for the agent-agnostic versus
+ agent-specific split.
diff --git a/docs/final-report.md b/docs/final-report.md
index 2285cf9..ceecdd3 100644
--- a/docs/final-report.md
+++ b/docs/final-report.md
@@ -1,4 +1,4 @@
-# Phase 6 — Final Report: keber/qa-framework
+# Phase 6 - Final Report: keber/qa-framework
**Date**: 2025-01-15
**Version analyzed**: 1.0.0
@@ -26,29 +26,29 @@ procedures documented in `MIGRATION-NOTES.md`.
## 2. Source Repository Findings
-### Repo A — redacted-repo-web
+### Repo A - redacted-repo-web
| Category | Finding |
|----------|---------|
| Stack | ASP.NET MVC 5 + Web API 2 + SQL Server |
| Frontend | jQuery + Select2 + toastr + SweetAlert2 + Metronic |
| Auth | RUT-based login (`#m_login_signin_submit`) |
-| QA maturity | Medium — spec files present, standards missing |
+| QA maturity | Medium - spec files present, standards missing |
| Automation | Sprint 40: 26 P0 tests (24 pass, 2 skip via DEF-001/DEF-002) |
| ADO | Referenced (Plan 21992/Suite 21993) but not reporter-integrated |
| Modules | 7 submodules documented (~160 TCs across 82 QA files) |
| Strengths | EXEC_IDX pattern, POM structure, multi-role auth, skip+DEF |
| Gaps | No `00-standards/`, no `08-azure-integration/`, no session summaries |
-### Repo B — redacted-repo
+### Repo B - redacted-repo
| Category | Finding |
|----------|---------|
| Stack | Blazor WebAssembly + .NET + Radzen components |
| Auth | Email-based (`#email-input` / `#password-input`) |
-| QA maturity | High — complete standards folder, ADO fully integrated |
+| QA maturity | High - complete standards folder, ADO fully integrated |
| Automation | 67 automated TCs (53 pass / 2 fail / 12 skip) |
-| ADO | Full: Plans 22304/22794/22875, WI IDs 22957–23034, bi-directional sync |
+| ADO | Full: Plans 22304/22794/22875, WI IDs 22957-23034, bi-directional sync |
| Modules | 4 modules × 17+ submodules (~894 TCs) |
| Strengths | `00-standards/`, `08-azure-integration/`, module-registry, ado reporter |
| Gaps | No EXEC_IDX pattern, no POM convention, no session summaries |
@@ -61,9 +61,9 @@ procedures documented in `MIGRATION-NOTES.md`.
|---------|--------|----------|
| 6-file submodule spec pattern | Both | Core framework pattern |
| Playwright `@playwright/test` | Both | Standard automation library |
-| Priority levels P0–P3 | Both | Universal tagging via `@PX` |
+| Priority levels P0-P3 | Both | Universal tagging via `@PX` |
| `test.skip()` + DEF reference | Repo A | Adopted as standard skip convention |
-| `06-defects/` folder | Repo A | Optional — recommended without ADO |
+| `06-defects/` folder | Repo A | Optional - recommended without ADO |
| `00-standards/` folder | Repo B | Adopted; templates included |
| `08-azure-integration/` folder | Repo B | Optional; fully documented |
| `module-registry.json` | Repo B | Included in ADO integration |
@@ -85,7 +85,7 @@ procedures documented in `MIGRATION-NOTES.md`.
| RUT-based username field | Repo A | Parameterized via `QA_LOGIN_EMAIL_SELECTOR` |
| `#email-input` / `#password-input` | Repo B | Parameterized via env vars |
| ADO Plan IDs (21992, 22304, etc.) | Both | Parameterized via `QA_ADO_PLAN_ID` |
-| ADO WI IDs (22957–23034) | Repo B | Injected per-project via inject-ado-ids.ps1 |
+| ADO WI IDs (22957-23034) | Repo B | Injected per-project via inject-ado-ids.ps1 |
| Specific module names/selectors | Both | Remain in project-specific spec files |
| Sprint numbers | Both | Remain in project-specific test plans |
| Metronic theme selectors | Repo A | Project-specific; documented in Repo A specs |
@@ -110,7 +110,7 @@ Fifteen formal decisions were made and documented in `docs/generalization-decisi
| §8 | POM pattern | Recommended (Repo A); inline acceptable |
| §9 | Standards folder | Adopted from Repo B (more complete) |
| §10 | Session summaries | New addition to both repos' patterns |
-| §11 | Priority levels | P0–P3 universal; `@P0` grep-tag convention |
+| §11 | Priority levels | P0-P3 universal; `@P0` grep-tag convention |
| §12 | `08-azure-integration/` | Optional; fully documented scripts |
| §13 | CI pipeline | Parameterized `azure-pipeline-qa.yml` |
| §14 | Framework distribution | npm package (`keber/qa-framework`) |
@@ -122,23 +122,23 @@ Fifteen formal decisions were made and documented in `docs/generalization-decisi
```
qa-framework/
-├── package.json ← npm package, CLI entry
-├── qa-framework.config.json ← example project config
+├── package.json <- npm package, CLI entry
+├── qa-framework.config.json <- example project config
├── README.md
├── CHANGELOG.md
├── MIGRATION-NOTES.md
│
├── docs/
-│ ├── architecture.md ← 4-layer component map
-│ ├── comparison-matrix.md ← Phase 1 artifact
-│ ├── generalization-decisions.md ← Phase 2 artifact (15 decisions)
-│ ├── installation.md ← 3 install options
-│ ├── spec-driven-philosophy.md ← Core methodology
-│ ├── folder-structure-guide.md ← Full folder reference
-│ ├── usage-with-agent.md ← Agent prompt patterns
-│ └── final-report.md ← This file
+│ ├── architecture.md <- 4-layer component map
+│ ├── comparison-matrix.md <- Phase 1 artifact
+│ ├── generalization-decisions.md <- Phase 2 artifact (15 decisions)
+│ ├── installation.md <- 3 install options
+│ ├── spec-driven-philosophy.md <- Core methodology
+│ ├── folder-structure-guide.md <- Full folder reference
+│ ├── usage-with-agent.md <- Agent prompt patterns
+│ └── final-report.md <- This file
│
-├── agent-instructions/ ← Consumed by Copilot agent
+├── agent-instructions/ <- Consumed by Copilot agent
│ ├── 00-module-analysis.md
│ ├── 01-spec-generation.md
│ ├── 02-test-plan-generation.md
@@ -148,7 +148,7 @@ qa-framework/
│ └── 06-maintenance.md
│
├── templates/
-│ ├── specification/ ← 6-file submodule template set
+│ ├── specification/ <- 6-file submodule template set
│ │ ├── 00-inventory.md
│ │ ├── 01-business-rules.md
│ │ ├── 02-workflows.md
@@ -188,7 +188,7 @@ qa-framework/
│ ├── 00-inventory.md
│ └── suppliers-create.spec.ts
│
-└── scripts/ ← CLI implementation
+└── scripts/ <- CLI implementation
├── cli.js
├── init.js
├── generate.js
diff --git a/docs/folder-structure-guide.md b/docs/folder-structure-guide.md
index 8fb3caf..e24b255 100644
--- a/docs/folder-structure-guide.md
+++ b/docs/folder-structure-guide.md
@@ -1,6 +1,6 @@
# docs/folder-structure-guide.md
-## `qa/` Directory Structure — Full Reference Guide
+## `qa/` Directory Structure - Full Reference Guide
This document explains the purpose, contents, and conventions for every folder in the `qa/` directory.
@@ -10,25 +10,28 @@ This document explains the purpose, contents, and conventions for every folder i
```
qa/
-├── README.md ← Living master index (project-maintained)
-├── AGENT-NEXT-STEPS.md ← Active sprint queue for the agent (project-maintained)
-├── QA-STRUCTURE-GUIDE.md ← Copy of this guide (installed by framework)
-├── qa-framework.config.json ← Project configuration
+├── README.md <- Living master index (project-maintained)
+├── AGENT-NEXT-STEPS.md <- Active sprint queue for the agent (project-maintained)
+├── QA-STRUCTURE-GUIDE.md <- Copy of this guide (installed by framework)
+├── qa-framework.config.json <- Project configuration
│
-├── 00-standards/ ← Naming conventions and artifact templates
-├── 01-specifications/ ← Functional specifications per module
-├── 02-test-plans/ ← Plan de Pruebas per sprint/module (plan + TC table + steps)
-├── 03-test-cases/ ← Standalone test case documents
-├── 04-test-data/ ← Test data definitions and factories
-├── 05-test-execution/ ← Execution reports and results
-├── 06-defects/ ← Defect tracking (optional)
-├── 07-automation/ ← Automation code (Playwright, etc.)
-├── 08-azure-integration/ ← Azure DevOps integration (optional)
-└── memory/ ← Project QA learnings (project-maintained)
+├── 00-standards/ <- Naming conventions and artifact templates
+├── 01-specifications/ <- Functional specifications per module
+├── 02-test-plans/ <- Plan de Pruebas per sprint/module (plan + TC table + steps)
+├── 03-test-cases/ <- Standalone test case documents
+├── 04-test-data/ <- Test data definitions and factories
+├── 05-test-execution/ <- Execution reports and results
+├── 06-defects/ <- Defect tracking (optional)
+├── 07-automation/ <- Automation code (Playwright, etc.)
+├── 08-azure-integration/ <- Azure DevOps integration (optional)
+└── memory/ <- Project QA learnings (project-maintained)
```
Agent skills live in `.github/skills/` (not inside `qa/`). See the `.github/skills/` section below.
+`init`/`upgrade` also generate `.claude/rules/qa-framework.md` and `.claude/commands/qa-*.md`
+(Claude Code equivalents of the Copilot instructions/skills - see the section below).
+
---
## Dual-Track Pipeline
@@ -37,35 +40,35 @@ All pipeline paths share the same input: `01-specifications/`. From there, work
two tracks that can run in parallel or independently depending on team maturity.
```
-01-specifications/ (source of truth — what the app does and what must be tested)
+01-specifications/ (source of truth - what the app does and what must be tested)
│
├─── Automation track ──────────────────────────────────────────────────
│ │
│ ▼
- │ 02-test-plans/sprints/Sprint-{N}/ ← Plan de Pruebas (plan + TC table + steps)
+ │ 02-test-plans/sprints/Sprint-{N}/ <- Plan de Pruebas (plan + TC table + steps)
│ │ Stage 4 expands steps if needed
│ ▼
- │ 07-automation/e2e/tests/ ← Playwright .spec.ts files
+ │ 07-automation/e2e/tests/ <- Playwright .spec.ts files
│ │
│ ▼
- │ 05-test-execution/automated/ ← execution reports
+ │ 05-test-execution/automated/ <- execution reports
│ │
│ ▼
- │ 08-azure-integration/ ← ADO sync via playwright-azure-reporter
+ │ 08-azure-integration/ <- ADO sync via playwright-azure-reporter
│
└─── Manual track ──────────────────────────────────────────────────────
│
▼
- 02-test-plans/sprints/Sprint-{N}/ ← same Plan de Pruebas (shared artifact)
+ 02-test-plans/sprints/Sprint-{N}/ <- same Plan de Pruebas (shared artifact)
│
▼
- 03-test-cases/ (optional) ← standalone TC-*.md for complex/reusable TCs
+ 03-test-cases/ (optional) <- standalone TC-*.md for complex/reusable TCs
│
▼
- 05-test-execution/manual/ ← manual run evidence
+ 05-test-execution/manual/ <- manual run evidence
│
▼
- 08-azure-integration/ ← ADO sync via ado-integration skill
+ 08-azure-integration/ <- ADO sync via ado-integration skill
(reads Plan de Pruebas, creates TCs with steps)
```
@@ -73,21 +76,21 @@ two tracks that can run in parallel or independently depending on team maturity.
| Scenario | Entry point | Tracks active |
|---|---|---|
-| New system, no prior QA | Module analysis → spec generation | Automation (primary), Manual (as needed) |
-| Sprint with ADO stories | Stories → spec generation → test plan | Both, driven by sprint scope |
-| Maintenance/continuous improvement | Changed module → maintenance skill | Automation (update existing tests) |
+| New system, no prior QA | Module analysis -> spec generation | Automation (primary), Manual (as needed) |
+| Sprint with ADO stories | Stories -> spec generation -> test plan | Both, driven by sprint scope |
+| Maintenance/continuous improvement | Changed module -> maintenance skill | Automation (update existing tests) |
**Track selection guidance:**
- Use **automation track** when the goal is regression coverage and CI integration.
- Use **manual track** when: (a) team has manual testers who need step-by-step documents; (b) auditability or traceability to external standards is required; (c) features are not yet automatable.
-- Both tracks use the same TC-IDs from `01-specifications/*/05-test-scenarios.md` — traceability is preserved regardless of which track executes a given TC.
+- Both tracks use the same TC-IDs from `01-specifications/*/05-test-scenarios.md` - traceability is preserved regardless of which track executes a given TC.
- `04-test-data/` is optional in both tracks. Use it when test data factories or seeders are shared across multiple modules. Per-submodule data belongs in `01-specifications/*/04-test-data.md`.
---
## .github/skills/
-**Purpose**: 3-layer QA pipeline skills installed by the framework. Loaded by the agent on demand — only the relevant skill for the current task is loaded, not all skills at once.
+**Purpose**: 3-layer QA pipeline skills installed by the framework. Loaded by the agent on demand - only the relevant skill for the current task is loaded, not all skills at once.
**Installed by framework** via `npm install @keber/qa-framework`:
@@ -103,8 +106,25 @@ two tracks that can run in parallel or independently depending on team maturity.
| `qa-maintenance/` | 6 | Updating specs and tests after app changes |
Each skill folder contains:
-- `SKILL.md` — process outline (~400-600 tokens, always loaded when skill applies)
-- `references/` — detailed templates and code patterns (loaded on demand)
+- `SKILL.md` - process outline (~400-600 tokens, always loaded when skill applies)
+- `references/` - detailed templates and code patterns (loaded on demand)
+
+---
+
+## .claude/commands/ and .claude/rules/
+
+**Purpose**: Claude Code native equivalents of `.github/instructions/qa-framework.instructions.md`
+and `.github/skills/`, generated by `init`/`upgrade` in parallel with the Copilot artifacts
+(no agent detection - both are always generated).
+
+| File | Role |
+|---|---|
+| `.claude/rules/qa-framework.md` | Same 11 agent behavior rules + pipeline table as the Copilot instructions file. No frontmatter, so it loads unconditionally in every Claude Code session (equivalent to `applyTo: '**'`). |
+| `.claude/commands/qa-{name}.md` | One thin wrapper per skill folder under `.github/skills/`, discovered dynamically (not hardcoded). Each wrapper reads: `Read the full skill at .github/skills/qa-{name}/SKILL.md FIRST, then follow its instructions exactly`, plus the stage prerequisite. Invoked as `/qa-{name}` in a Claude Code session. |
+
+This is a "thin wrapper" pattern: the command never duplicates skill content, it only
+points back at `SKILL.md` as the single source of truth. Updating a skill automatically
+updates every command that references it - no regeneration needed, zero drift possible.
---
@@ -126,25 +146,25 @@ Each skill folder contains:
## 01-specifications/
-**Purpose**: Functional specifications — the primary source of truth for what the application does and what must be tested.
+**Purpose**: Functional specifications - the primary source of truth for what the application does and what must be tested.
**Structure**:
```
01-specifications/
-├── README.md ← Index of all modules
-├── shared/ ← Shared artifacts (sitemap, shared templates)
-│ ├── ui-menu-map.md ← Auto-generated from playwright session
-│ └── Instrucciones-analisis.md ← Checklist template for module analysis
+├── README.md <- Index of all modules
+├── shared/ <- Shared artifacts (sitemap, shared templates)
+│ ├── ui-menu-map.md <- Auto-generated from playwright session
+│ └── Instrucciones-analisis.md <- Checklist template for module analysis
│
└── module-{module-name}/
- ├── README.md ← Module index (submodule table + E2E flow)
+ ├── README.md <- Module index (submodule table + E2E flow)
└── submodule-{name}/
- ├── 00-inventory.md ← UI elements, routes, APIs, fields
- ├── 01-business-rules.md ← RN-* rules the system enforces
- ├── 02-workflows.md ← FL-* user flow diagrams (Mermaid/ASCII)
- ├── 03-roles-permissions.md ← Role matrix
- ├── 04-test-data.md ← Data shapes and prerequisites
- └── 05-test-scenarios.md ← TC-* test cases with priority and steps
+ ├── 00-inventory.md <- UI elements, routes, APIs, fields
+ ├── 01-business-rules.md <- RN-* rules the system enforces
+ ├── 02-workflows.md <- FL-* user flow diagrams (Mermaid/ASCII)
+ ├── 03-roles-permissions.md <- Role matrix
+ ├── 04-test-data.md <- Data shapes and prerequisites
+ └── 05-test-scenarios.md <- TC-* test cases with priority and steps
```
**Naming rules**:
@@ -153,7 +173,7 @@ Each skill folder contains:
- Module code: 2-6 uppercase letters (e.g., `OPER`, `PERS`, `ARCF`)
- TC ID: `TC-{MODULE}-{SUBMODULE}-{3-digit}` (e.g., `TC-OPER-CAT-001`)
-**TC target per submodule**: 50–85 test cases is considered a well-analyzed submodule.
+**TC target per submodule**: 50-85 test cases is considered a well-analyzed submodule.
---
@@ -166,8 +186,8 @@ Each skill folder contains:
├── README.md
└── sprints/
└── Sprint-{NNN}/
- ├── Plan-de-Pruebas-{proyecto}-Sprint-{NNN}-{modulo}.md ← one per module
- └── Plan-de-Pruebas-{proyecto}-Sprint-{NNN}.md ← consolidado (generated by @keber/ado-qa, not by agent)
+ ├── Plan-de-Pruebas-{proyecto}-Sprint-{NNN}-{modulo}.md <- one per module
+ └── Plan-de-Pruebas-{proyecto}-Sprint-{NNN}.md <- consolidado (generated by @keber/ado-qa, not by agent)
```
**Naming convention**: `Plan-de-Pruebas-{proyecto}-Sprint-{NNN}-{modulo}.md`
@@ -179,7 +199,7 @@ Each skill folder contains:
- Header (project, sprint, dates, ADO Test Plan link)
- Scope, strategy, preconditions, test data
- **Tabla de Pruebas**: TC-ID | Suite | Título | Descripción | Steps (numbered ` `-separated) | Resultado Esperado | Confirma (Task ID or N/A) | Tipo | Prioridad
-- Matriz de trazabilidad (Task ↔ N)
+- Matriz de trazabilidad (Task <-> N)
- Automation notes
**Legacy migration**: `upgrade` Section 6 moves `automated/` and `manual/` subdirectories to `sprints/legacy-*` non-destructively.
@@ -194,7 +214,7 @@ Each skill folder contains:
```
03-test-cases/
-├── README.md ← explains optional/legacy status
+├── README.md <- explains optional/legacy status
└── TC-{ID}-{title}.md
```
@@ -212,10 +232,10 @@ Each skill folder contains:
```
04-test-data/
├── README.md
-├── users.md ← Test user definitions (roles, env var references, NOT passwords)
-├── fixtures/ ← Static data files (JSON/Markdown)
-├── factories/ ← Dynamic data generation patterns (Markdown or TS files)
-└── seeders/ ← DB/API seed scripts for complex test setup
+├── users.md <- Test user definitions (roles, env var references, NOT passwords)
+├── fixtures/ <- Static data files (JSON/Markdown)
+├── factories/ <- Dynamic data generation patterns (Markdown or TS files)
+└── seeders/ <- DB/API seed scripts for complex test setup
```
**Critical rules**:
@@ -228,14 +248,14 @@ Each skill folder contains:
## 05-test-execution/
-**Purpose**: Evidence of test runs — reports, results, and screenshots.
+**Purpose**: Evidence of test runs - reports, results, and screenshots.
```
05-test-execution/
├── README.md
├── automated/
-│ ├── {YYYY-MM-DD_HH-MM-SS_desc}.md ← Execution report (human-readable summary)
-│ └── test-results/ ← Playwright output (gitignored if large)
+│ ├── {YYYY-MM-DD_HH-MM-SS_desc}.md <- Execution report (human-readable summary)
+│ └── test-results/ <- Playwright output (gitignored if large)
│ └── .last-run.json
└── manual/
├── exploratory/
@@ -271,8 +291,8 @@ Each skill folder contains:
- `resolved/` - closed by fix or formal decision
**Decision guide**:
-- ADO enabled → use ADO Work Items. `06-defects/` contains lightweight references only
-- ADO disabled → use this folder as primary defect tracker
+- ADO enabled -> use ADO Work Items. `06-defects/` contains lightweight references only
+- ADO disabled -> use this folder as primary defect tracker
- Either way: `test.skip()` references a defect ID so the skip is traceable
See `docs/generalization-decisions.md §7` for the full evaluation of this folder.
@@ -325,21 +345,21 @@ See `docs/generalization-decisions.md §7` for the full evaluation of this folde
```
08-azure-integration/
├── README.md
-├── AGENT-ADO-INTEGRATION.md ← Agent instructions for ADO operations
-├── PLAYWRIGHT-AZURE-REPORTER-PLAN.md ← Reporter setup and migration log
-├── module-registry.json ← Module → spec path → ADO plan/suite IDs
-├── ado-ids-mapping-{project}.json ← Complete TC → ADO WI ID registry
+├── AGENT-ADO-INTEGRATION.md <- Agent instructions for ADO operations
+├── PLAYWRIGHT-AZURE-REPORTER-PLAN.md <- Reporter setup and migration log
+├── module-registry.json <- Module -> spec path -> ADO plan/suite IDs
+├── ado-ids-mapping-{project}.json <- Complete TC -> ADO WI ID registry
├── pipelines/
-│ └── azure-pipeline-qa.yml ← CI/CD pipeline definition
+│ └── azure-pipeline-qa.yml <- CI/CD pipeline definition
└── scripts/
- ├── inject-ado-ids.ps1 ← Injects [ID] prefix into spec files
- ├── create-testplan-from-mapping.ps1 ← Creates ADO plan from mapping JSON
- └── sync-ado-titles.ps1 ← Syncs spec titles with ADO TC titles
+ ├── inject-ado-ids.ps1 <- Injects [ID] prefix into spec files
+ ├── create-testplan-from-mapping.ps1 <- Creates ADO plan from mapping JSON
+ └── sync-ado-titles.ps1 <- Syncs spec titles with ADO TC titles
```
---
-## `qa/README.md` — The Living Index
+## `qa/README.md` - The Living Index
The `qa/README.md` is the primary human-readable project status document. It should be updated whenever:
- A new module is analyzed
@@ -352,46 +372,46 @@ Minimum contents:
- Quick-start commands for common tasks
- Active blockers section
- Last execution results summary
-- **`## Sprint History`** section — completed sprint checklists moved here from `AGENT-NEXT-STEPS.md`
+- **`## Sprint History`** section - completed sprint checklists moved here from `AGENT-NEXT-STEPS.md`
Template: `templates/qa-readme.md`
---
-## `qa/AGENT-NEXT-STEPS.md` — Sprint Queue
+## `qa/AGENT-NEXT-STEPS.md` - Sprint Queue
The `AGENT-NEXT-STEPS.md` is the agent's task queue for the current sprint. It is read by the agent at the start of every conversation.
**Design principles (SRP + KISS)**:
-- Contains **one active sprint only** — no history, no completed checklists
+- Contains **one active sprint only** - no history, no completed checklists
- Three sections maximum: module status table, active sprint checklist, context references
- Does not repeat standing instructions already in `qa-framework.instructions.md`
- Detailed environment notes and patterns belong in `qa/memory/`, referenced from here
-When a sprint completes, the agent **moves** the completed checklist to `qa/README.md → ## Sprint History` and **deletes** that section from this file.
+When a sprint completes, the agent **moves** the completed checklist to `qa/README.md -> ## Sprint History` and **deletes** that section from this file.
Template: `templates/agent-next-steps.md`
---
-## `qa/memory/` — Project QA Learnings
+## `qa/memory/` - Project QA Learnings
Project-maintained folder for accumulated knowledge about the application under test: environment quirks, DOM patterns, Playwright gotchas, sprint-specific discoveries.
```
qa/memory/
-├── INDEX.md ← Required entry point — index of all files
-├── {technology}-patterns.md ← Framework/stack-specific patterns (e.g., playwright-blazor-wasm-patterns.md)
-├── {sprint-name}-discovery.md ← DOM/UI findings for an in-progress sprint
-└── {sprint-name}-lessons.md ← Retrospective patterns after a sprint completes
+├── INDEX.md <- Required entry point - index of all files
+├── {technology}-patterns.md <- Framework/stack-specific patterns (e.g., playwright-blazor-wasm-patterns.md)
+├── {sprint-name}-discovery.md <- DOM/UI findings for an in-progress sprint
+└── {sprint-name}-lessons.md <- Retrospective patterns after a sprint completes
```
-### `qa/memory/INDEX.md` — Required
+### `qa/memory/INDEX.md` - Required
The `INDEX.md` is the **gate** that the agent reads before loading any other memory file. Its purpose is to let the agent load only what is relevant to the current task, rather than loading all files.
```markdown
-# Memory Index — {PROJECT_NAME}
+# Memory Index - {PROJECT_NAME}
| File | Topic | When to load |
|---|---|---|
@@ -403,7 +423,7 @@ The `INDEX.md` is the **gate** that the agent reads before loading any other mem
**Rules for `qa/memory/`**:
- Always update `INDEX.md` when adding or modifying a memory file
- Sprint-specific discovery files (`sprint-N-discovery.md`) are for the duration of that sprint; consolidate long-term patterns into a technology-specific file afterwards
-- Never load all memory files unconditionally — always read `INDEX.md` first and select by relevance
+- Never load all memory files unconditionally - always read `INDEX.md` first and select by relevance
---
@@ -414,4 +434,4 @@ Optionally, place `SESSION-SUMMARY-YYYY-MM-DD.md` at the `qa/` root after each m
- Record any blockers encountered
- Provide the starting point for the next session
-This is especially valuable for agents — a session summary provides continuity across multiple chat sessions.
+This is especially valuable for agents - a session summary provides continuity across multiple chat sessions.
diff --git a/docs/installation.md b/docs/installation.md
index 4ff8719..8770194 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -13,7 +13,7 @@
---
-## Option A — npm install (recommended)
+## Option A - npm install (recommended)
```bash
npm install --save-dev @keber/qa-framework
@@ -57,7 +57,7 @@ qa/
---
-## Option B — Clone or copy (no npm registry)
+## Option B - Clone or copy (no npm registry)
```bash
# From your project root
@@ -69,7 +69,7 @@ node tools/qa-framework/scripts/cli.js init
---
-## Option C — Manual scaffold (advanced)
+## Option C - Manual scaffold (advanced)
If you prefer to control exactly what gets created:
@@ -156,9 +156,37 @@ Edit `qa/qa-framework.config.json` (or `qa-framework.config.json` at project roo
**Never** put `testPlanId`, `suiteId`, or `ADO_PAT` in the config file directly.
Use environment variables:
-- `ADO_PAT` — Personal Access Token
-- `ADO_PLAN_ID` — Test Plan ID (can also go in `module-registry.json`)
-- `ADO_SUITE_ID` — Suite ID
+- `ADO_PAT` - Personal Access Token
+
+#### Optional: ANALISIS/PLAN agent mode (Claude Code only)
+
+Gated by `integrations.azureDevOps.sprintCycle.enabled`. When `true`, `init`/`upgrade`
+generate four Claude Code subagents (`.claude/agents/qa-analisis.md`, `qa-plan.md`,
+`qa-asesoria.md`, `qa-informe-resultados.md`) for a sprint-centric manual testing
+workflow parallel to the 6-stage pipeline. See
+[docs/usage-with-agent.md](usage-with-agent.md) for details.
+
+```json
+{
+ "integrations": {
+ "azureDevOps": {
+ "enabled": true,
+ "sprintCycle": {
+ "enabled": true,
+ "sprintDurationDays": 8,
+ "manualTestingTimeboxDays": 2,
+ "dateFormat": "dd-mm-aaaa",
+ "timezone": "America/Santiago"
+ }
+ }
+ }
+}
+```
+
+All `sprintCycle` fields are optional; omitted fields fall back to neutral framework
+defaults (8-day sprint, 2-day manual testing timebox, `dd-mm-aaaa`, `America/Santiago`).
+- `ADO_PLAN_ID` - Test Plan ID (can also go in `module-registry.json`)
+- `ADO_SUITE_ID` - Suite ID
---
@@ -244,6 +272,6 @@ The `upgrade` command:
1. Checks the current framework version in your project
2. Shows a diff of changed template and instruction files
3. Prompts before overwriting any file that has local modifications
-4. Never touches `01-specifications/`, `02-test-plans/`, `03-test-cases/`, `04-test-data/`, `05-test-execution/`, `06-defects/` — only framework-owned files are updated
+4. Never touches `01-specifications/`, `02-test-plans/`, `03-test-cases/`, `04-test-data/`, `05-test-execution/`, `06-defects/` - only framework-owned files are updated
See `MIGRATION-NOTES.md` for version-specific migration instructions.
diff --git a/docs/skills-architecture.md b/docs/skills-architecture.md
index 11a3c37..88f847c 100644
--- a/docs/skills-architecture.md
+++ b/docs/skills-architecture.md
@@ -16,9 +16,9 @@ The 3-layer skill architecture reduces this to ~500 tokens for a typical task, l
| Layer | Location | Size | Purpose |
|---|---|---|---|
-| 1 — Descriptor | YAML frontmatter in `SKILL.md` | ~50 tokens | Name + description; used for relevance detection |
-| 2 — Process outline | `SKILL.md` body | ~400–600 tokens | Step-by-step process without code; always loaded when skill applies |
-| 3 — Reference material | `references/*.md` | ~500–1500 tokens each | Detailed templates, code patterns, decision trees; loaded on demand |
+| 1 - Descriptor | YAML frontmatter in `SKILL.md` | ~50 tokens | Name + description; used for relevance detection |
+| 2 - Process outline | `SKILL.md` body | ~400-600 tokens | Step-by-step process without code; always loaded when skill applies |
+| 3 - Reference material | `references/*.md` | ~500-1500 tokens each | Detailed templates, code patterns, decision trees; loaded on demand |
The agent reads Layer 2 to understand what to do, then loads only the specific `references/` files it needs for the current step.
@@ -26,7 +26,7 @@ The agent reads Layer 2 to understand what to do, then loads only the specific `
## Pipeline Sequence
-Skills map to the QA pipeline stages defined in `copilot-instructions.md`. The file acts as a **pipeline sequencer** — it defines which stage comes before which, and what the prerequisite is. Skills are the implementation of each stage.
+Skills map to the QA pipeline stages defined in `copilot-instructions.md`. The file acts as a **pipeline sequencer** - it defines which stage comes before which, and what the prerequisite is. Skills are the implementation of each stage.
```
Stage 1: qa-module-analysis (no prerequisite)
@@ -61,12 +61,12 @@ Optional (any time after Stage 4):
|---|---|---|
| `qa-module-analysis` | Stage 1 process (4 phases) | `exploration-checklist.md`, `spec-file-formats.md` |
| `qa-spec-generation` | Stage 2 rules + anti-patterns | `spec-file-formats.md` (shared with qa-module-analysis) |
-| `qa-test-plan` | Stage 3 — Plan de Pruebas generation | `plan-de-pruebas-template.md` (includes priority + feasibility rules) |
-| `qa-test-cases` | Stage 4 — TC document rules | `test-case-template.md` |
-| `qa-automation` | Stage 5 — patterns + completion checklist | `patterns.md`, `config-checklist.md` |
-| `qa-test-stabilization` | Stage 5b — 8 steps + confidence scoring | `classification-protocol.md` (includes report template) |
-| `qa-ado-integration` | Optional ADO sync — 6 steps | `scripts-and-config.md` |
-| `qa-maintenance` | Stage 6 — update rules | `update-rules.md` |
+| `qa-test-plan` | Stage 3 - Plan de Pruebas generation | `plan-de-pruebas-template.md` (includes priority + feasibility rules) |
+| `qa-test-cases` | Stage 4 - TC document rules | `test-case-template.md` |
+| `qa-automation` | Stage 5 - patterns + completion checklist | `patterns.md`, `config-checklist.md` |
+| `qa-test-stabilization` | Stage 5b - 8 steps + confidence scoring | `classification-protocol.md` (includes report template) |
+| `qa-ado-integration` | Optional ADO sync - 6 steps | `scripts-and-config.md` |
+| `qa-maintenance` | Stage 6 - update rules | `update-rules.md` |
---
@@ -85,7 +85,7 @@ When a consumer project runs `npm install @keber/qa-framework`, the `postinstall
SKILL.md
...
-→ copies to →
+-> copies to ->
{consumer-project}/.github/skills/
qa-module-analysis/
@@ -96,7 +96,7 @@ When a consumer project runs `npm install @keber/qa-framework`, the `postinstall
...
```
-The copy uses `writeIfMissing` — it will not overwrite files that already exist in the consumer project. This allows consumer projects to customize their `.github/skills/` without losing changes on re-install.
+The copy uses `writeIfMissing` - it will not overwrite files that already exist in the consumer project. This allows consumer projects to customize their `.github/skills/` without losing changes on re-install.
---
@@ -112,7 +112,7 @@ Consumer projects can extend the skills by adding skill folders directly to `.gi
Describe when this skill should be triggered.
---
```
-2. Add the skill body following the same 4-section structure: prerequisite → steps → rules → outputs
+2. Add the skill body following the same 4-section structure: prerequisite -> steps -> rules -> outputs
3. Optionally add `references/*.md` for detailed templates
4. Reference the skill from `copilot-instructions.md` if it's part of your pipeline
@@ -124,13 +124,13 @@ Project-specific skills are not overwritten by `npm install` updates.
| Former file | New skill | Token reduction |
|---|---|---|
-| `agent-instructions/00-module-analysis.md` | `.github/skills/qa-module-analysis/SKILL.md` | ~2,500 → ~500 + on-demand references |
-| `agent-instructions/01-spec-generation.md` | `.github/skills/qa-spec-generation/SKILL.md` | ~2,800 → ~400 + shared references |
-| `agent-instructions/02-test-plan-generation.md` | `.github/skills/qa-test-plan/SKILL.md` | ~2,200 → ~400 + test-plan-template |
-| `agent-instructions/03-test-case-generation.md` | `.github/skills/qa-test-cases/SKILL.md` | ~2,100 → ~400 + tc-template |
-| `agent-instructions/04-automation-generation.md` | `.github/skills/qa-automation/SKILL.md` | ~3,500 → ~500 + patterns + config |
-| `agent-instructions/04b-test-stabilization.md` | `.github/skills/qa-test-stabilization/SKILL.md` | ~3,800 → ~500 + classification |
-| `agent-instructions/05-ado-integration.md` | `.github/skills/qa-ado-integration/SKILL.md` | ~2,400 → ~400 + scripts |
-| `agent-instructions/06-maintenance.md` | `.github/skills/qa-maintenance/SKILL.md` | ~1,700 → ~400 + update-rules |
-
-**Total context reduction**: ~21,000 tokens → ~3,500 tokens (Layer 2 only) + selective Layer 3 loading
+| `agent-instructions/00-module-analysis.md` | `.github/skills/qa-module-analysis/SKILL.md` | ~2,500 -> ~500 + on-demand references |
+| `agent-instructions/01-spec-generation.md` | `.github/skills/qa-spec-generation/SKILL.md` | ~2,800 -> ~400 + shared references |
+| `agent-instructions/02-test-plan-generation.md` | `.github/skills/qa-test-plan/SKILL.md` | ~2,200 -> ~400 + test-plan-template |
+| `agent-instructions/03-test-case-generation.md` | `.github/skills/qa-test-cases/SKILL.md` | ~2,100 -> ~400 + tc-template |
+| `agent-instructions/04-automation-generation.md` | `.github/skills/qa-automation/SKILL.md` | ~3,500 -> ~500 + patterns + config |
+| `agent-instructions/04b-test-stabilization.md` | `.github/skills/qa-test-stabilization/SKILL.md` | ~3,800 -> ~500 + classification |
+| `agent-instructions/05-ado-integration.md` | `.github/skills/qa-ado-integration/SKILL.md` | ~2,400 -> ~400 + scripts |
+| `agent-instructions/06-maintenance.md` | `.github/skills/qa-maintenance/SKILL.md` | ~1,700 -> ~400 + update-rules |
+
+**Total context reduction**: ~21,000 tokens -> ~3,500 tokens (Layer 2 only) + selective Layer 3 loading
diff --git a/docs/spec-driven-philosophy.md b/docs/spec-driven-philosophy.md
index cc0b799..fc76ea1 100644
--- a/docs/spec-driven-philosophy.md
+++ b/docs/spec-driven-philosophy.md
@@ -1,6 +1,6 @@
# docs/spec-driven-philosophy.md
-## Spec-Driven Automated Testing — Core Philosophy
+## Spec-Driven Automated Testing - Core Philosophy
This document explains the methodology that underpins the entire `qa-framework`.
@@ -34,7 +34,7 @@ A test case with origin `PENDING-CODE` or `BLOCKED-PERMISSIONS` must be in `test
## The Spec-Before-Automation Rule
```
-Specification exists FIRST → Automation comes second
+Specification exists FIRST -> Automation comes second
```
Automation artifacts (spec files, page objects) always reference a spec TC-ID. It is never acceptable to write a Playwright test without a corresponding TC in `05-test-scenarios.md`.
@@ -90,10 +90,10 @@ E2E tests should NOT try to cover:
| Priority | Meaning | Target in automation |
|---|---|---|
-| **P0** | Critical — system unusable without this | Must be automated |
-| **P1** | High — significant impact if broken | Should be automated |
-| **P2** | Medium — moderate impact | Automate if feasible |
-| **P3** | Low — minor impact | Manual testing acceptable |
+| **P0** | Critical - system unusable without this | Must be automated |
+| **P1** | High - significant impact if broken | Should be automated |
+| **P2** | Medium - moderate impact | Automate if feasible |
+| **P3** | Low - minor impact | Manual testing acceptable |
In the automation suite, P0 tests form the **smoke suite** that runs on every CI build.
P1 tests run on scheduled runs or before each release.
@@ -165,6 +165,6 @@ This framework supports both:
| Maintenance after code change | Agent + Human review |
The handoff between agent and human should always happen at a known checkpoint:
-- After the spec set is produced → human reviews before automation is written
-- After automation is written → human reviews coverage mapping
-- After a defect is filed → human decides priority and fix approach
+- After the spec set is produced -> human reviews before automation is written
+- After automation is written -> human reviews coverage mapping
+- After a defect is filed -> human decides priority and fix approach
diff --git a/docs/usage-with-agent.md b/docs/usage-with-agent.md
index 340ba0d..c9d34cf 100644
--- a/docs/usage-with-agent.md
+++ b/docs/usage-with-agent.md
@@ -1,12 +1,14 @@
# docs/usage-with-agent.md
-## Using `qa-framework` with an IDE Agent (GitHub Copilot)
+## Using `qa-framework` with an IDE Agent (GitHub Copilot or Claude Code)
---
## Overview
-This framework is designed so that an IDE agent (such as GitHub Copilot in VS Code) can:
+`init` and `upgrade` always generate artifacts for both GitHub Copilot and Claude Code
+in parallel - there is no agent detection. This framework is designed so that either
+IDE agent can:
1. Understand the QA structure by reading the agent instruction files
2. Navigate the `qa/` directory predictably
@@ -42,9 +44,9 @@ This framework is designed so that an IDE agent (such as GitHub Copilot in VS Co
---
-## Setting Up Agent Instructions in VS Code
+## Setting Up Agent Instructions in VS Code (GitHub Copilot)
-### Option A — Workspace instructions file (recommended)
+### Option A - Workspace instructions file (recommended)
`init` generates `.github/instructions/qa-framework.instructions.md` automatically. It contains the full
pipeline sequencer with 11 agent behavior rules, the QA pipeline table, and ADO detection.
@@ -56,7 +58,7 @@ To regenerate after an upgrade:
npx qa-framework upgrade
```
-### Option B — Reference skills directly in the agent conversation
+### Option B - Reference skills directly in the agent conversation
Paste a skill path into the Copilot chat:
@@ -64,7 +66,7 @@ Paste a skill path into the Copilot chat:
Read .github/skills/qa-module-analysis/SKILL.md and analyze the module at {URL}
```
-### Option C — Individual task prompts
+### Option C - Individual task prompts
For each major QA task, ask the agent to load the relevant skill:
@@ -75,6 +77,77 @@ tests for the submodule {name}
---
+## Setting Up Agent Instructions in Claude Code
+
+`init` and `upgrade` generate two Claude-Code-native artifacts automatically, no manual
+setup needed:
+
+- `.claude/rules/qa-framework.md` - the Claude Code equivalent of the Copilot
+ `.instructions.md` file. It has no frontmatter, which means it loads unconditionally in
+ every session (the same effect as `applyTo: '**'`). It contains the same 11 agent
+ behavior rules and pipeline table as the Copilot instructions, with every skill
+ reference expressed as a `/qa-{name}` slash command instead of "load this file".
+- `.claude/commands/qa-{name}.md` - one thin wrapper per skill, generated dynamically
+ from whatever folders exist under `.github/skills/` at install/upgrade time. Each
+ wrapper is a single instruction: `Read the full skill at .github/skills/qa-{name}/SKILL.md
+ FIRST, then follow its instructions exactly`, plus the stage prerequisite. There is zero
+ duplication of skill content and therefore zero drift - if a skill is updated, every
+ command that points to it picks up the change with no regeneration needed.
+
+### Using a command
+
+In a Claude Code session, invoke the pipeline stage directly:
+
+```
+/qa-module-analysis
+```
+
+Claude reads `.github/skills/qa-module-analysis/SKILL.md` and follows it exactly, the
+same way the Copilot pipeline table routes to that file.
+
+### Regenerating after an upgrade
+
+```bash
+npx qa-framework upgrade
+```
+
+This refreshes both `.claude/commands/qa-*.md` and `.claude/rules/qa-framework.md` the
+same safe way it refreshes the Copilot artifacts (framework-owned, always overwritten
+with the current version; nothing else under `.claude/` is touched).
+
+---
+
+## Optional: ANALISIS/PLAN Mode (Claude Code only, ADO-gated)
+
+Some teams run a sprint-centric manual testing workflow in parallel with the 6-stage
+pipeline: an analysis document, a test plan trace-linked to Azure DevOps work items,
+ad-hoc QA advisory chat, and a sprint closing results report. This mode is:
+
+- **Optional** - off by default.
+- **Gated** by `integrations.azureDevOps.sprintCycle.enabled` in
+ `qa/qa-framework.config.json` (it requires Azure DevOps enabled, since three of the
+ four agents depend on ADO work items or an ADO-generated execution report).
+- **Claude Code only, by design, not by omission.** Each agent is a Claude Code subagent
+ with its own `tools`/`model` frontmatter (e.g. `qa-asesoria` intentionally has no
+ `Write`/`Bash`). GitHub Copilot has no equivalent primitive, so no `.github/` artifact
+ is generated for this mode.
+
+When enabled, `init`/`upgrade` generate four subagents under `.claude/agents/`:
+
+| Agent | Purpose |
+|---|---|
+| `qa-analisis.md` | Analysis document: test universe, automation feasibility, P0-P3 prioritization, excluded universe, traceability matrix |
+| `qa-plan.md` | Default mode: executive summary + a trace-linked test case table, executable within the configured manual-testing timebox |
+| `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 |
+| `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 |
+
+Sprint duration, manual-testing timebox, date format, and timezone are parameterized
+from `integrations.azureDevOps.sprintCycle` (with neutral defaults - 8-day sprint,
+2-day timebox, `dd-mm-aaaa`, `America/Santiago` - when a field is omitted). See
+[docs/installation.md](installation.md) for the config shape.
+
+---
+
## Typical Agent Session Workflow
### Session 1: Initial Module Analysis
@@ -124,7 +197,7 @@ tests for the submodule {name}
### DO
-- Read `qa/AGENT-NEXT-STEPS.md` at the start of each conversation (auto-enforced by copilot-instructions.md)
+- Read `qa/AGENT-NEXT-STEPS.md` at the start of each conversation (auto-enforced by `.github/instructions/qa-framework.instructions.md` for Copilot, or `.claude/rules/qa-framework.md` for Claude Code)
- Load the relevant skill from `.github/skills/` before starting any QA task
- Save all artifacts in the exact path specified by `qa/QA-STRUCTURE-GUIDE.md`
- Use TC-ID, RN-ID, FL-ID naming consistently
@@ -181,7 +254,8 @@ This performs:
## Continuing from a Previous Session
The agent reads `qa/AGENT-NEXT-STEPS.md` automatically at the start of every conversation
-(enforced by `.github/copilot-instructions.md` rule 0). For additional context:
+(enforced by rule 0 in `.github/instructions/qa-framework.instructions.md` for Copilot,
+or `.claude/rules/qa-framework.md` for Claude Code). For additional context:
```
"Read qa/README.md and qa/memory/INDEX.md, then tell me where we left off."
diff --git a/examples/module-example/suppliers/00-inventory.md b/examples/module-example/suppliers/00-inventory.md
index 60f8f3c..5a5b4f6 100644
--- a/examples/module-example/suppliers/00-inventory.md
+++ b/examples/module-example/suppliers/00-inventory.md
@@ -1,4 +1,4 @@
-# Suppliers — Inventory
+# Suppliers - Inventory
**Module**: Suppliers (SUP)
**Submodule**: Create / Edit / List
diff --git a/examples/module-example/suppliers/suppliers-create.spec.ts b/examples/module-example/suppliers/suppliers-create.spec.ts
index 2df2293..ff868cb 100644
--- a/examples/module-example/suppliers/suppliers-create.spec.ts
+++ b/examples/module-example/suppliers/suppliers-create.spec.ts
@@ -15,16 +15,16 @@
import { test, expect } from '@playwright/test';
-// EXEC_IDX: unique per minute-window — prevents data collisions between runs
+// EXEC_IDX: unique per minute-window - prevents data collisions between runs
const EXEC_IDX = Math.floor(Date.now() / 60_000) % 100_000;
test.describe('Suppliers > Create @P0', () => {
/**
- * [TC-SUP-CR-001] Create supplier — happy path @P0
+ * [TC-SUP-CR-001] Create supplier - happy path @P0
* Verifies the complete create flow for a valid supplier.
*/
- test('[TC-SUP-CR-001] Create supplier — happy path @P0', async ({ page }) => {
+ test('[TC-SUP-CR-001] Create supplier - happy path @P0', async ({ page }) => {
const supplierName = `QA-Supplier-${EXEC_IDX}`;
const supplierRut = `${EXEC_IDX}-K`;
const supplierEmail = `qa-supplier-${EXEC_IDX}@example.com`;
@@ -57,10 +57,10 @@ test.describe('Suppliers > Create @P0', () => {
});
/**
- * [TC-SUP-CR-002] Create supplier — required fields validation @P0
+ * [TC-SUP-CR-002] Create supplier - required fields validation @P0
* Verifies that the form prevents submission when required fields are empty.
*/
- test('[TC-SUP-CR-002] Create supplier — required fields validation @P0', async ({ page }) => {
+ test('[TC-SUP-CR-002] Create supplier - required fields validation @P0', async ({ page }) => {
await page.goto('/suppliers');
await page.locator('button:has-text("New Supplier")').click();
await expect(page.locator('.supplier-modal')).toBeVisible();
@@ -79,12 +79,12 @@ test.describe('Suppliers > Create @P0', () => {
});
/**
- * [TC-SUP-CR-003] Create supplier — duplicate RUT rejected @P0
+ * [TC-SUP-CR-003] Create supplier - duplicate RUT rejected @P0
*
- * NOTE: test.skip active — DEF-001: Duplicate RUT check not enforced server-side.
+ * NOTE: test.skip active - DEF-001: Duplicate RUT check not enforced server-side.
* Reactivate when ADO #99001 is resolved.
*/
- test('[TC-SUP-CR-003] Create supplier — duplicate RUT rejected @P0', async ({ page }) => {
+ test('[TC-SUP-CR-003] Create supplier - duplicate RUT rejected @P0', async ({ page }) => {
test.skip(true,
'DEF-001: Duplicate RUT validation not enforced. Reactivate when ADO #99001 is resolved.'
);
diff --git a/integrations/ado-powershell/README.md b/integrations/ado-powershell/README.md
index 4eed86b..381481e 100644
--- a/integrations/ado-powershell/README.md
+++ b/integrations/ado-powershell/README.md
@@ -1,4 +1,4 @@
-# Integration — ADO PowerShell Scripts
+# Integration - ADO PowerShell Scripts
PowerShell scripts for Azure DevOps Test Plan management.
All scripts are parameterized and project-agnostic.
diff --git a/integrations/ado-powershell/scripts/sync-ado-titles.ps1 b/integrations/ado-powershell/scripts/sync-ado-titles.ps1
index b406716..f334752 100644
--- a/integrations/ado-powershell/scripts/sync-ado-titles.ps1
+++ b/integrations/ado-powershell/scripts/sync-ado-titles.ps1
@@ -61,7 +61,7 @@ foreach ($entry in $mapping) {
Select-Object -First 1
if (-not $specFile) {
- Write-Warning " [NOT FOUND] $($entry.specFile) — skipping WI $adoId"
+ Write-Warning " [NOT FOUND] $($entry.specFile) - skipping WI $adoId"
$skipped++
continue
}
@@ -84,7 +84,7 @@ foreach ($entry in $mapping) {
if ($PSCmdlet.ShouldProcess("WI #$adoId", "Update title to: $newTitle")) {
Invoke-RestMethod -Method Patch -Uri $patchUrl -Headers $headers -Body $body | Out-Null
- Write-Host " [SYNCED] WI #$adoId → $newTitle"
+ Write-Host " [SYNCED] WI #$adoId -> $newTitle"
$synced++
}
}
diff --git a/integrations/playwright/README.md b/integrations/playwright/README.md
index 0a9a391..1a6053e 100644
--- a/integrations/playwright/README.md
+++ b/integrations/playwright/README.md
@@ -1,4 +1,4 @@
-# Integration — Playwright
+# Integration - Playwright
This directory documents how `@playwright/test` is configured within the
`keber/qa-framework` opinionated setup.
@@ -40,9 +40,9 @@ suite execution.
## Debugging checklist
-1. `PWDEBUG=1 npx playwright test` — step through in inspector
-2. `--headed` — watch the browser
-3. `--trace on` — record full trace; open with `npx playwright show-trace`
+1. `PWDEBUG=1 npx playwright test` - step through in inspector
+2. `--headed` - watch the browser
+3. `--trace on` - record full trace; open with `npx playwright show-trace`
4. Increase `actionTimeout` if the app has slow server-side rendering
5. Add `await page.waitForLoadState('networkidle')` before assertions on
dynamically loaded content
diff --git a/meta/iteration-01-issue-backlog.md b/meta/iteration-01-issue-backlog.md
index 5e3d8d7..e46a795 100644
--- a/meta/iteration-01-issue-backlog.md
+++ b/meta/iteration-01-issue-backlog.md
@@ -1,10 +1,10 @@
-# Framework Iteration 01 — Pre-Release Issue Backlog
+# Framework Iteration 01 - Pre-Release Issue Backlog
**Document type**: Pre-release defect and design gap registry
**Date**: 2026-03-05
**Framework version**: 1.0.0 (unreleased)
**Source**: Code-level review of all agent-instructions/, scripts/, templates/, and docs/ files
-**Status**: Open — must be addressed before v1.0.0 release unless explicitly deferred
+**Status**: Open - must be addressed before v1.0.0 release unless explicitly deferred
> Items in this list are independent from the pipeline and entry-mode gaps documented in
> `iteration-01-process-analysis.md`. Both documents together constitute the full known
@@ -26,7 +26,7 @@
---
-### ISSUE-01 — Spec path is inconsistent across three files
+### ISSUE-01 - Spec path is inconsistent across three files
**Severity**: 🔴 Critical
**Affects**: `agent-instructions/04-automation-generation.md`, `scripts/init.js`, `scripts/validate.js`
@@ -49,7 +49,7 @@ path.
#### Fix instructions
1. Decide the canonical path. The simpler convention (`qa/{moduleKey}/{subKey}/`) is already
- what `init.js` creates and what `validate.js` scans — prefer this one.
+ what `init.js` creates and what `validate.js` scans - prefer this one.
2. In `agent-instructions/04-automation-generation.md`, find every reference to
`qa/01-specifications/` and replace with `qa/{module-key}/{submodule-key}/`.
3. In the spec file JSDoc template in `04-automation-generation.md`, update:
@@ -60,13 +60,13 @@ path.
* @spec qa/{module-kebab}/{submodule-kebab}/05-test-scenarios.md
```
4. Search all other files in `agent-instructions/`, `docs/`, and `README.md` for
- `01-specifications` — update any remaining occurrences.
+ `01-specifications` - update any remaining occurrences.
5. If `01-specifications/` was intentional (to keep specs separate from automation), document
this decision explicitly and update `init.js` and `validate.js` to match.
---
-### ISSUE-02 — `EXEC_IDX` has a silent collision window
+### ISSUE-02 - `EXEC_IDX` has a silent collision window
**Severity**: 🔴 Critical
**Affects**: `agent-instructions/04-automation-generation.md`, `templates/automation-scaffold/fixtures/test-helpers.ts`, `templates/specification/04-test-data.md`
@@ -110,7 +110,7 @@ application bug rather than a test data problem.
---
-### ISSUE-03 — Security posture inconsistency: `.fill()` vs `page.evaluate()` for passwords
+### ISSUE-03 - Security posture inconsistency: `.fill()` vs `page.evaluate()` for passwords
**Severity**: 🔴 Critical
**Affects**: `templates/automation-scaffold/global-setup.ts`, `agent-instructions/04-automation-generation.md`
@@ -121,7 +121,7 @@ application bug rather than a test data problem.
passwords, preventing them from appearing in Playwright traces:
```typescript
-// In 04-automation-generation.md — SECURE
+// In 04-automation-generation.md - SECURE
await page.evaluate(
([sel, pwd]) => { (document.querySelector(sel) as HTMLInputElement).value = pwd; },
[process.env.QA_LOGIN_PASSWORD_SELECTOR, process.env.QA_USER_PASSWORD]
@@ -131,7 +131,7 @@ await page.evaluate(
`templates/automation-scaffold/global-setup.ts` uses `.fill()`:
```typescript
-// In global-setup.ts — INSECURE: password appears in traces
+// In global-setup.ts - INSECURE: password appears in traces
await page.locator(passwordSelector).fill(password);
```
@@ -155,7 +155,7 @@ implementation than one reading `04-automation-generation.md`. Playwright traces
---
-### ISSUE-04 — No test data teardown strategy
+### ISSUE-04 - No test data teardown strategy
**Severity**: 🟠 Significant
**Affects**: `templates/specification/04-test-data.md`, `agent-instructions/04-automation-generation.md`, `agent-instructions/06-maintenance.md`
@@ -189,7 +189,7 @@ This is a shared-state problem that compounds non-linearly with module count and
---
-### ISSUE-05 — `COVERAGE-MAPPING.md` has no schema or template
+### ISSUE-05 - `COVERAGE-MAPPING.md` has no schema or template
**Severity**: 🟠 Significant
**Affects**: `agent-instructions/04-automation-generation.md`, `templates/`
@@ -207,7 +207,7 @@ agent will invent a different structure, making it:
1. Create `templates/coverage-mapping.md` with the following defined columns:
```markdown
- # Coverage Mapping — {MODULE} > {SUBMODULE}
+ # Coverage Mapping - {MODULE} > {SUBMODULE}
| TC-ID | Title | Priority | Spec file | Playwright file | Test function name | Status | Notes |
|-------|-------|----------|-----------|-----------------|-------------------|--------|-------|
@@ -220,7 +220,7 @@ agent will invent a different structure, making it:
---
-### ISSUE-06 — Automation feasibility has no update trigger
+### ISSUE-06 - Automation feasibility has no update trigger
**Severity**: 🟠 Significant
**Affects**: `templates/test-plan.md`, `agent-instructions/06-maintenance.md`
@@ -249,7 +249,7 @@ non-automatable TCs that may actually be automatable.
---
-### ISSUE-07 — ADO inject/sync has no rollback and breaks on title restructuring
+### ISSUE-07 - ADO inject/sync has no rollback and breaks on title restructuring
**Severity**: 🟠 Significant
**Affects**: `integrations/ado-powershell/scripts/inject-ado-ids.ps1`, `integrations/ado-powershell/scripts/sync-ado-titles.ps1`
@@ -257,13 +257,13 @@ non-automatable TCs that may actually be automatable.
#### Description
`inject-ado-ids.ps1` is idempotent (won't double-inject) but has no rollback. If the script
-runs and then Stage 3.5 (stabilization) causes test title restructuring — TC consolidation,
-splitting, or significant rename — the injected numeric prefix becomes stale:
+runs and then Stage 3.5 (stabilization) causes test title restructuring - TC consolidation,
+splitting, or significant rename - the injected numeric prefix becomes stale:
- The title and the WI ID are now mismatched in both the spec file and ADO
- `sync-ado-titles.ps1` silently skips entries where the title match pattern fails
- TC consolidation (two tests merged into one) leaves an orphaned ADO WI with no spec file
- reference — it never reports results, but is counted in the plan
+ reference - it never reports results, but is counted in the plan
There is no detection mechanism for any of these states.
@@ -287,7 +287,7 @@ There is no detection mechanism for any of these states.
---
-### ISSUE-08 — Agent instructions are pipeline-sequence-unaware
+### ISSUE-08 - Agent instructions are pipeline-sequence-unaware
**Severity**: 🟡 Moderate
**Affects**: All files in `agent-instructions/`
@@ -315,9 +315,9 @@ the file header:
|-------|-------|
| Stage number | 3 |
| Stage name | Test Case Generation |
-| Preceding stage | Stage 2 — Test Plan Generation (`02-test-plan-generation.md`) |
-| Following stage | Stage 3 — Automation Generation (`04-automation-generation.md`) |
-| Can be skipped? | Yes — skip if TCs are documented sufficiently in `05-test-scenarios.md` |
+| Preceding stage | Stage 2 - Test Plan Generation (`02-test-plan-generation.md`) |
+| Following stage | Stage 3 - Automation Generation (`04-automation-generation.md`) |
+| Can be skipped? | Yes - skip if TCs are documented sufficiently in `05-test-scenarios.md` |
| Required inputs | Approved `test-plan.md` or populated `05-test-scenarios.md` |
| Produced outputs | `templates/test-case.md` instance per complex TC |
| Exit criterion | All P0 and P1 TCs have either a spec row or a standalone TC document |
@@ -327,7 +327,7 @@ Apply this block to all 7 instruction files (00 through 06).
---
-### ISSUE-09 — `03-test-case-generation.md` has no clear trigger and undefined audience
+### ISSUE-09 - `03-test-case-generation.md` has no clear trigger and undefined audience
**Severity**: 🟡 Moderate
**Affects**: `agent-instructions/03-test-case-generation.md`
@@ -336,7 +336,7 @@ Apply this block to all 7 instruction files (00 through 06).
The instruction file says "use when a TC needs more detail than the table row provides."
This is a judgment call with no objective criterion. In practice:
-- Agents generating automation (Stage 4) don't need standalone TC documents — `05-test-scenarios.md`
+- Agents generating automation (Stage 4) don't need standalone TC documents - `05-test-scenarios.md`
rows + `04-automation-generation.md` patterns are sufficient
- Human manual testers do need step-by-step documents
- The planning-to-ADO stream (Mode B) needs TC-level detail to create `Ready` ADO WIs
@@ -359,12 +359,12 @@ The file is trying to serve three audiences with one document and a vague trigge
- All TCs are automatable P0/P1 with ≤4 steps (covered by spec rows + automation directly)
- The project is in Mode A (discovery-first) and automation is the only delivery channel
```
-2. Reference this from `agent-instructions/02-test-plan-generation.md` — when the plan
+2. Reference this from `agent-instructions/02-test-plan-generation.md` - when the plan
identifies Manual-only TCs, it should explicitly trigger Stage 3.
---
-### ISSUE-10 — No module granularity decision rule
+### ISSUE-10 - No module granularity decision rule
**Severity**: 🟡 Moderate
**Affects**: `agent-instructions/00-module-analysis.md`, `docs/spec-driven-philosophy.md`
@@ -388,12 +388,12 @@ Add a `## Granularity Rules` section to `agent-instructions/00-module-analysis.m
## Granularity Rules
**Module** = a top-level navigation section in the application (menu item, major feature area).
-A module code is 3–6 uppercase letters.
+A module code is 3-6 uppercase letters.
**Submodule** = a distinct view, CRUD entity, or workflow within a module. It maps to:
- One primary database entity (one Create/Read/Update/Delete surface)
- One distinct workflow (approval, import, export as a standalone process)
-- NOT a sub-tab or secondary panel within a view — those are covered by the parent submodule
+- NOT a sub-tab or secondary panel within a view - those are covered by the parent submodule
**Sizing heuristic**: A well-scoped submodule produces between 8 and 40 test cases.
- Fewer than 8: consider merging with a sibling submodule
@@ -405,7 +405,7 @@ merging (merging requires retiring TC-IDs; splitting only requires adding new on
---
-### ISSUE-11 — `validate.js` does not check TypeScript compilation
+### ISSUE-11 - `validate.js` does not check TypeScript compilation
**Severity**: 🟡 Moderate
**Affects**: `scripts/validate.js`
@@ -434,7 +434,7 @@ signal.
errors.push(`TypeScript compilation errors found:\n${result.stdout}`);
}
} else {
- warnings.push('[STRICT] No tsconfig.json found in 07-automation/ — TypeScript check skipped');
+ warnings.push('[STRICT] No tsconfig.json found in 07-automation/ - TypeScript check skipped');
}
}
```
@@ -443,7 +443,7 @@ signal.
---
-### ISSUE-12 — Non-browser testing scope is undefined
+### ISSUE-12 - Non-browser testing scope is undefined
**Severity**: 🟡 Moderate
**Affects**: `docs/spec-driven-philosophy.md`, `docs/folder-structure-guide.md`, `README.md`
@@ -482,7 +482,7 @@ The absence creates ad-hoc divergent decisions across projects.
---
-### ISSUE-13 — TC Origin classification missing `SPRINT-AGREED` tag for Mode B
+### ISSUE-13 - TC Origin classification missing `SPRINT-AGREED` tag for Mode B
**Severity**: 🟡 Moderate
**Affects**: `docs/spec-driven-philosophy.md`, `agent-instructions/00-module-analysis.md`, `agent-instructions/03-test-case-generation.md`, `templates/specification/05-test-scenarios.md`
@@ -490,13 +490,13 @@ The absence creates ad-hoc divergent decisions across projects.
#### Description
`spec-driven-philosophy.md` defines three TC origin tags for traceability:
-- `UI-OBSERVED` — discovered from clicking through a live application
-- `PENDING-CODE` — feature not yet deployed; TC written ahead of code
-- `BLOCKED-PERMISSIONS` — TC cannot be run due to a missing role or environment
+- `UI-OBSERVED` - discovered from clicking through a live application
+- `PENDING-CODE` - feature not yet deployed; TC written ahead of code
+- `BLOCKED-PERMISSIONS` - TC cannot be run due to a missing role or environment
This taxonomy was designed for **Mode A** (discovery-first, live app as source of truth).
**Mode B** (planning-first, sprint meeting + ADO Work Items as source) has no equivalent tag.
-TCs drafted at planning time from WI descriptions — before the feature is built — require a
+TCs drafted at planning time from WI descriptions - before the feature is built - require a
fundamentally different reliability assumption: the spec may be wrong, the step sequence is
hypothetical, and the acceptance criteria haven't been validated against actual UI yet.
@@ -512,7 +512,7 @@ that these TCs have higher probability of requiring revision after the build lan
|-----|---------|
| `UI-OBSERVED` | Derived from live application interaction (Mode A) |
| `SPRINT-AGREED` | Derived from sprint planning meeting / ADO Work Item description (Mode B). Steps are hypothetical until verified against the built feature. |
- | `PENDING-CODE` | Feature not yet deployed; derivation mode irrelevant — code doesn't exist yet |
+ | `PENDING-CODE` | Feature not yet deployed; derivation mode irrelevant - code doesn't exist yet |
| `BLOCKED-PERMISSIONS` | Cannot be executed due to missing access |
```
2. In `templates/specification/05-test-scenarios.md`, add `Origin` as a column in the TC table
@@ -521,12 +521,12 @@ that these TCs have higher probability of requiring revision after the build lan
generated TCs must be tagged `SPRINT-AGREED` and must include a
`> ⚠️ Review after deploy: steps not yet validated against live UI` callout block.
4. In `agent-instructions/00-module-analysis.md`, at the point where COVERAGE-MAPPING is
- seeded, note that Mode B modules will have 100% `SPRINT-AGREED` TCs — this is expected
+ seeded, note that Mode B modules will have 100% `SPRINT-AGREED` TCs - this is expected
and should not be treated as a quality gap.
---
-### ISSUE-14 — No connection between `04-test-data.md` and provisioning code
+### ISSUE-14 - No connection between `04-test-data.md` and provisioning code
**Severity**: 🟠 Significant
**Affects**: `templates/specification/04-test-data.md`, `agent-instructions/04-automation-generation.md`
@@ -535,8 +535,8 @@ that these TCs have higher probability of requiring revision after the build lan
`04-test-data.md` is a spec document that describes the data shapes a submodule needs
(entities, field ranges, precondition records). `04-automation-generation.md` describes
-`beforeAll` provisioning blocks inside `.spec.ts` files. The two documents are related —
-one describes *what* data; the other produces *the code that creates it* — but the framework
+`beforeAll` provisioning blocks inside `.spec.ts` files. The two documents are related -
+one describes *what* data; the other produces *the code that creates it* - but the framework
never draws this connection.
Consequences:
@@ -554,7 +554,7 @@ Consequences:
```markdown
| Field | Type | Constraints | Example value | Provisioning reference |
|-------|------|-------------|---------------|----------------------|
- | name | string | 3–100 chars, unique | QA-Supplier-{EXEC_IDX} | `helpers.createSupplier({ name })` |
+ | name | string | 3-100 chars, unique | QA-Supplier-{EXEC_IDX} | `helpers.createSupplier({ name })` |
```
2. In `agent-instructions/04-automation-generation.md`, add a step before "Write the spec
file" instructing the agent to:
@@ -567,7 +567,7 @@ Consequences:
---
-### ISSUE-15 — CI pipeline template existence is unverified
+### ISSUE-15 - CI pipeline template existence is unverified
**Severity**: 🟡 Moderate
**Affects**: `agent-instructions/05-ado-integration.md`, `integrations/ado-powershell/pipelines/` (expected location)
@@ -581,7 +581,7 @@ and test outcome reporting. It is not confirmed whether this file exists inside
If the file does not exist:
- Agents following `05-ado-integration.md` will hit a dead reference and halt or improvise
-- The CI integration is effectively undocumented for new projects — each project re-invents
+- The CI integration is effectively undocumented for new projects - each project re-invents
the pipeline YAML
- `validate.js --strict` has no check for this file, so the gap is invisible
@@ -600,7 +600,7 @@ If the file does not exist:
---
-### ISSUE-16 — Examples folder is incomplete
+### ISSUE-16 - Examples folder is incomplete
**Severity**: 🟠 Significant
**Affects**: `examples/module-example/suppliers/`
@@ -623,7 +623,7 @@ A complete submodule requires 6 numbered spec files plus automation:
- `suppliers-create.spec.ts` ✅ (exists, but incomplete without the full spec set)
Agents and engineers onboarding to the framework rely on examples to understand correct
-output. An incomplete example is worse than no example — it implies the missing spec files
+output. An incomplete example is worse than no example - it implies the missing spec files
either don't exist or don't matter.
#### Fix instructions
@@ -631,7 +631,7 @@ either don't exist or don't matter.
1. Create `examples/module-example/suppliers/01-business-rules.md` using the corresponding
template, populated with realistic rules (e.g., "Supplier name must be unique within
active suppliers", "RUT must pass Chilean checksum validation").
-2. Create `examples/module-example/suppliers/02-user-stories.md` with 3–5 user stories
+2. Create `examples/module-example/suppliers/02-user-stories.md` with 3-5 user stories
covering the create-supplier workflow.
3. Create `examples/module-example/suppliers/03-ui-screens.md` with placeholder screenshots
and annotated field descriptions for the create-supplier form.
@@ -644,7 +644,7 @@ either don't exist or don't matter.
---
-### ISSUE-17 — `validate.js` checks `06-defects/` but not its required subdirectories
+### ISSUE-17 - `validate.js` checks `06-defects/` but not its required subdirectories
**Severity**: 🟡 Moderate
**Affects**: `scripts/validate.js`, `scripts/init.js`, `agent-instructions/06-maintenance.md`
@@ -660,7 +660,7 @@ qa/{moduleKey}/{subKey}/06-defects/
`agent-instructions/06-maintenance.md` references files in `06-defects/open/` by path
(e.g., "Move this file to `06-defects/resolved/`"). However, `validate.js` only checks
-that a `06-defects/` folder exists at the top level — it does NOT verify that
+that a `06-defects/` folder exists at the top level - it does NOT verify that
`06-defects/open/` and `06-defects/resolved/` exist as subdirectories.
Consequence: an `init.js` run that partially fails (e.g., creates `06-defects/` but not
@@ -689,7 +689,7 @@ moving defect files.
---
-### ISSUE-18 — Defect files placed at `06-defects/` root are not detected by `validate.js`
+### ISSUE-18 - Defect files placed at `06-defects/` root are not detected by `validate.js`
**Severity**: 🟡 Moderate
**Affects**: `scripts/validate.js`, `agent-instructions/06-maintenance.md`
@@ -702,7 +702,7 @@ convention: a defect file created directly at `06-defects/DEF-001.md` (skipping
subdirectory) will pass validation without warning.
This is distinct from ISSUE-17 (which concerns the subdirectories themselves not existing).
-This issue concerns defect files that exist but are in the wrong location — they will not
+This issue concerns defect files that exist but are in the wrong location - they will not
be found by agents scanning `06-defects/open/` for actionable defects.
#### Fix instructions
@@ -721,7 +721,7 @@ be found by agents scanning `06-defects/open/` for actionable defects.
}
}
```
-2. This should produce a warning (not an error) — files at the root are not dangerously
+2. This should produce a warning (not an error) - files at the root are not dangerously
wrong, just misplaced.
3. Update the `## Conventions` section of `06-maintenance.md` to make this rule explicit:
> Defect files MUST live in `06-defects/open/` or `06-defects/resolved/`. Files placed
@@ -729,7 +729,7 @@ be found by agents scanning `06-defects/open/` for actionable defects.
---
-### ISSUE-19 — Session summaries have no consolidated pipeline state view
+### ISSUE-19 - Session summaries have no consolidated pipeline state view
**Severity**: 🟡 Moderate
**Affects**: `templates/session-summary.md`, `agent-instructions/` (all files)
@@ -756,7 +756,7 @@ There is no single source of truth for pipeline state. This creates several fail
1. Add a **Pipeline State Tracker** table to `templates/session-summary.md` as the first
block written at every new session:
```markdown
- ## Pipeline State — {MODULE} > {SUBMODULE}
+ ## Pipeline State - {MODULE} > {SUBMODULE}
| Stage | Name | Status | Last updated | Notes |
|-------|------|--------|-------------|-------|
@@ -764,17 +764,17 @@ There is no single source of truth for pipeline state. This creates several fail
| 1 | Module Analysis | ✅ Complete | YYYY-MM-DD | |
| 2 | Test Plan Generation | ✅ Complete | YYYY-MM-DD | |
| 3 | Test Case Generation | ⏳ In progress | YYYY-MM-DD | |
- | 3.5 | Test Stabilization | ⬜ Not started | — | |
- | 4 | Automation Generation | ⬜ Not started | — | |
- | 5 | ADO Integration | ⬜ Not started | — | |
- | 6 | Maintenance | ⬜ Not started | — | |
+ | 3.5 | Test Stabilization | ⬜ Not started | - | |
+ | 4 | Automation Generation | ⬜ Not started | - | |
+ | 5 | ADO Integration | ⬜ Not started | - | |
+ | 6 | Maintenance | ⬜ Not started | - | |
```
2. In every agent instruction file, add a **first step** before any analysis: "Open or
create `session-summary.md` for this submodule. Update the Pipeline State Tracker table
to reflect current known state. Mark the current stage as ⏳ In progress."
3. At session end (or when switching stages), instruct agents to update the tracker, mark
the current stage ✅ Complete, and identify the next stage.
-4. If a submodule has no `session-summary.md`, this is an error — `validate.js` should
+4. If a submodule has no `session-summary.md`, this is an error - `validate.js` should
warn about its absence for any submodule that has been partially initialized (has spec
files but no summary).
@@ -783,7 +783,7 @@ There is no single source of truth for pipeline state. This creates several fail
> **Consolidation note**: Items 2, 6, and 7 from the original 21-item analysis were merged
> into **ISSUE-01** (they all concern the same three-way spec path inconsistency: the
> `@spec` annotation format, the `init.js` directory creation path, and the `validate.js`
-> scan pattern). This accounts for the apparent count discrepancy (21 items → 19 issues).
+> scan pattern). This accounts for the apparent count discrepancy (21 items -> 19 issues).
---
@@ -806,7 +806,7 @@ There is no single source of truth for pipeline state. This creates several fail
| ISSUE-13 | 🟡 Moderate | TC Origin missing `SPRINT-AGREED` tag for Mode B | `spec-driven-philosophy.md`, `05-test-scenarios.md`, `03-test-case-generation.md` | No |
| ISSUE-14 | 🟠 Significant | No connection between `04-test-data.md` and provisioning code | `04-test-data.md`, `04-automation-generation.md` | Yes |
| ISSUE-15 | 🟡 Moderate | CI pipeline template existence unverified | `05-ado-integration.md`, `integrations/ado-powershell/pipelines/` | No |
-| ISSUE-16 | 🟠 Significant | Examples folder incomplete — 4 of 6 spec files missing | `examples/module-example/suppliers/` | Yes |
+| ISSUE-16 | 🟠 Significant | Examples folder incomplete - 4 of 6 spec files missing | `examples/module-example/suppliers/` | Yes |
| ISSUE-17 | 🟡 Moderate | `validate.js` doesn't check `06-defects/open/` and `/resolved/` | `validate.js`, `init.js` | No |
| ISSUE-18 | 🟡 Moderate | Defect files at `06-defects/` root not detected by `validate.js` | `validate.js`, `06-maintenance.md` | No |
| ISSUE-19 | 🟡 Moderate | Session summaries lack consolidated pipeline state view | `templates/session-summary.md`, all `agent-instructions/` | No |
diff --git a/meta/iteration-01-process-analysis.md b/meta/iteration-01-process-analysis.md
index f61e28d..7431141 100644
--- a/meta/iteration-01-process-analysis.md
+++ b/meta/iteration-01-process-analysis.md
@@ -1,9 +1,9 @@
-# Framework Iteration 01 — Process Analysis & Design Findings
+# Framework Iteration 01 - Process Analysis & Design Findings
**Document type**: Meta-analysis / Framework retrospective
**Date**: 2026-03-05
**Framework version reviewed**: 1.0.0
-**Status**: Draft — for use as input to framework iteration 2
+**Status**: Draft - for use as input to framework iteration 2
---
@@ -36,10 +36,10 @@ receiving new sprint-driven features.
at runtime due to selector hallucination, flow assumption failures, and timing issues.
5. Intermediate artifacts are **load-bearing for agents** even when redundant for humans, but
- only when each artifact adds a genuine transformation — not a restatement. Some current
+ only when each artifact adds a genuine transformation - not a restatement. Some current
artifacts (test-plan.md vs 05-test-scenarios.md) are close to restatements.
-### Current state — pros and cons
+### Current state - pros and cons
| Pros | Cons |
|------|------|
@@ -64,7 +64,7 @@ receiving new sprint-driven features.
## Detailed Analysis
-### 1. The Pipeline Model Is Correct — The Input Model Is Not
+### 1. The Pipeline Model Is Correct - The Input Model Is Not
The framework correctly identifies the pipeline as a transformation chain from application to
ADO test run. The stage outputs are well-defined and the intermediate artifact chain is sound.
@@ -112,18 +112,18 @@ Mode B (planning-driven) has a built-in temporal problem that the framework curr
```mermaid
timeline
- title Sprint lifecycle — information availability
+ title Sprint lifecycle - information availability
Sprint Planning : WIs created
: Brief written
: Test plan intent (aspirational)
- : ADO TCs → Design status
+ : ADO TCs -> Design status
Development sprint : Feature built
: Selectors unknown to QA
: Steps are hypotheses
QA Release : App deployed to QA env
: Selectors now observable
: Flows verifiable
- : ADO TCs can become → Ready
+ : ADO TCs can become -> Ready
Test Execution : CI runs
: ADO results published
```
@@ -154,13 +154,13 @@ The actual operational flow currently practiced is:
```
Agent generates .spec.ts files
- ↓
+ v
A significant fraction don't run (selector issues, flow failures, timing, auth)
- ↓
+ v
Manual triage + PROMPT_Exhaustive_test_revision applied
- ↓
+ v
Iterative fix loop until ≥90% pass
- ↓
+ v
Committed tests
```
@@ -202,7 +202,7 @@ The agent needs:
- At minimum, one successful manual navigation of each flow to be automated
If any of these are absent, the agent should produce **spec stubs** with `test.todo()` markers
-and stop — not generate full tests that will fail at runtime and require a separate recovery loop.
+and stop - not generate full tests that will fail at runtime and require a separate recovery loop.
```mermaid
flowchart LR
@@ -221,24 +221,24 @@ flowchart LR
---
-### 5. Intermediate Artifacts — When Redundancy Is Justified
+### 5. Intermediate Artifacts - When Redundancy Is Justified
The discussion established a useful distinction:
**Justified redundancy** (each file extracts a different abstraction from the same observations):
```
-00-inventory.md → what exists (elements, endpoints)
-01-business-rules.md → why it works that way (constraints, logic)
-02-workflows.md → how users move through it (sequences)
-03-roles-permissions.md → who can do what
-04-test-data.md → what data is needed to test it
-05-test-scenarios.md → what should be tested
+00-inventory.md -> what exists (elements, endpoints)
+01-business-rules.md -> why it works that way (constraints, logic)
+02-workflows.md -> how users move through it (sequences)
+03-roles-permissions.md -> who can do what
+04-test-data.md -> what data is needed to test it
+05-test-scenarios.md -> what should be tested
```
**Questionable redundancy** (restatement with metadata reordering):
```
-05-test-scenarios.md → lists TCs with priority
-test-plan.md → reorganizes same TCs with risk/feasibility added
+05-test-scenarios.md -> lists TCs with priority
+test-plan.md -> reorganizes same TCs with risk/feasibility added
```
For agent-driven workflows, all intermediate outputs serve as **resumption points** that protect
@@ -256,7 +256,7 @@ Ground truth (inventory + scenarios) requires human review on every app change.
Currently, to determine where a module stands in the pipeline, an agent or human must read
multiple files and infer the state. There is no single authoritative signal.
-This creates a continuity problem across sessions — `session-summary.md` captures what happened
+This creates a continuity problem across sessions - `session-summary.md` captures what happened
in one session, but not the stage-level status of the module globally.
A lightweight **module status tracker** would solve this. It could be as simple as a table in
@@ -266,19 +266,19 @@ a `qa/00-standards/pipeline-status.md` file, or a structured field at the top of
Example structure:
```markdown
-## Pipeline status — [Module > Submodule]
+## Pipeline status - [Module > Submodule]
| Stage | Status | Last updated | Artifact |
|-------|--------|-------------|---------|
| 0 Bootstrap | ✅ Done | 2026-01-10 | qa-framework.config.json |
| 1 Discovery | ✅ Done | 2026-02-15 | suppliers/00-inventory.md |
| 2 Planning (intent) | ✅ Done | 2026-02-20 | 05-test-plans/suppliers-intent.md |
-| 2 Planning (concrete) | ⏳ Pending QA release | — | — |
-| 3 Automation | ⏳ Blocked by Stage 2 concrete | — | — |
-| 3.5 Stabilization | ⏳ Not started | — | — |
-| 4 ADO Wiring | ⏳ Not started | — | — |
-| 5 Execution | ⏳ Not started | — | — |
-| 6 Review | ⏳ Not started | — | — |
+| 2 Planning (concrete) | ⏳ Pending QA release | - | - |
+| 3 Automation | ⏳ Blocked by Stage 2 concrete | - | - |
+| 3.5 Stabilization | ⏳ Not started | - | - |
+| 4 ADO Wiring | ⏳ Not started | - | - |
+| 5 Execution | ⏳ Not started | - | - |
+| 6 Review | ⏳ Not started | - | - |
```
---
@@ -290,7 +290,7 @@ Example structure:
---
-### Step 1 — Define two entry modes in the framework root documentation
+### Step 1 - Define two entry modes in the framework root documentation
**Target files**: `README.md`, `docs/architecture.md`
@@ -303,23 +303,23 @@ pipeline diagram in `docs/architecture.md` to show two entry arrows.
---
-### Step 2 — Create `agent-instructions/00-sprint-intake.md`
+### Step 2 - Create `agent-instructions/00-sprint-intake.md`
This is the Mode B equivalent of `00-module-analysis.md`. It must define:
- **Input**: sprint WI IDs, meeting brief or transcription summary, ADO plan ID
- **Process**: extract scope, map WIs to submodules, infer general test steps from acceptance criteria
- **Output**: populated 6-file spec set (with `test-plan-intent.md` instead of full concrete spec)
-- **Explicit limitation**: selectors and exact steps are placeholders until QA release — replace
+- **Explicit limitation**: selectors and exact steps are placeholders until QA release - replace
with `TODO: verify after QA release` comments in any selector-level content
- **ADO action**: create Test Cases with status `Design`, NOT `Ready`
---
-### Step 3 — Split `templates/test-plan.md` into two variants
+### Step 3 - Split `templates/test-plan.md` into two variants
Create:
-- `templates/test-plan-intent.md` — planning-time version; general steps; acceptance criteria focus
-- `templates/test-plan-concrete.md` — post-release version; exact selectors; observable steps; data shapes
+- `templates/test-plan-intent.md` - planning-time version; general steps; acceptance criteria focus
+- `templates/test-plan-concrete.md` - post-release version; exact selectors; observable steps; data shapes
Add a header field to each:
```markdown
@@ -334,7 +334,7 @@ Update `agent-instructions/02-test-plan-generation.md` to:
---
-### Step 4 — Create `agent-instructions/04b-test-stabilization.md`
+### Step 4 - Create `agent-instructions/04b-test-stabilization.md`
Promote the content of `references/PROMPT_Exhaustive_test_revision` into a formal agent
instruction file. The file must define:
@@ -344,7 +344,7 @@ instruction file. The file must define:
- **Process**:
1. Run full suite; capture output
2. Classify each failure by root cause (selector / flow / timing / auth / data)
- 3. Fix in priority order: auth → flow → selector → timing → data
+ 3. Fix in priority order: auth -> flow -> selector -> timing -> data
4. Re-run after each fix class; do not fix all at once
5. Iterate until pass rate ≥90% or all remaining failures are documented with `test.skip()` + DEF reference
- **Exit criterion**: ≥90% pass rate; every skip has a DEF reference; no silent failures
@@ -353,7 +353,7 @@ instruction file. The file must define:
---
-### Step 5 — Add prerequisite check to `agent-instructions/04-automation-generation.md`
+### Step 5 - Add prerequisite check to `agent-instructions/04-automation-generation.md`
At the top of the file, before any generation instructions, add a **Prerequisites Gate** section:
@@ -368,29 +368,29 @@ Check all of the following before generating any `.spec.ts` content:
- [ ] At least one target flow has been manually traced in the browser
If ANY prerequisite is unmet:
-- Generate stub `.spec.ts` files with `test.todo('TODO: verify after QA release — [step description]')`
+- Generate stub `.spec.ts` files with `test.todo('TODO: verify after QA release - [step description]')`
- Add a `## Blocked` section to `session-summary.md` listing what is missing
- Do NOT generate full assertion-level tests
```
---
-### Step 6 — Add pipeline state tracker to `templates/session-summary.md`
+### Step 6 - Add pipeline state tracker to `templates/session-summary.md`
Add a `## Pipeline Status` table to the session summary template (see example in section 6 of
this document). The table must be updated at the end of every session by the agent. This makes
-resumption unambiguous — any agent starting a new session reads this table and knows exactly
+resumption unambiguous - any agent starting a new session reads this table and knows exactly
which stage to enter.
---
-### Step 7 — Clarify ground truth vs derived artifacts in `docs/spec-driven-philosophy.md`
+### Step 7 - Clarify ground truth vs derived artifacts in `docs/spec-driven-philosophy.md`
Add a section `## Artifact Types: Ground Truth vs Derived` that defines:
-- **Ground truth**: `00-inventory.md`, `05-test-scenarios.md` — must be manually reviewed on
+- **Ground truth**: `00-inventory.md`, `05-test-scenarios.md` - must be manually reviewed on
every app change; these are the source of correctness for all downstream artifacts
-- **Derived**: `test-plan.md`, `execution-report.md`, `COVERAGE-MAPPING.md` — regenerable on
+- **Derived**: `test-plan.md`, `execution-report.md`, `COVERAGE-MAPPING.md` - regenerable on
demand; should not be manually maintained between sprints; flag when stale
Add a **staleness rule**: if the app has been updated and `00-inventory.md` has not been
@@ -399,7 +399,7 @@ execution cycle.
---
-### Step 8 — Add generation quality constraint to `agent-instructions/04-automation-generation.md`
+### Step 8 - Add generation quality constraint to `agent-instructions/04-automation-generation.md`
After the prerequisites gate, add a **Generation Strategy** section:
diff --git a/openspec/changes/archive/.gitkeep b/openspec/changes/archive/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/openspec/config.yaml b/openspec/config.yaml
new file mode 100644
index 0000000..a6bcf28
--- /dev/null
+++ b/openspec/config.yaml
@@ -0,0 +1,49 @@
+schema: spec-driven
+
+context: |
+ Project: qa-framework (@keber/qa-framework)
+ Tech stack: Node.js (>=18) npm package, CommonJS, no framework/bundler.
+ Type: CLI/library authoring tool. main=scripts/index.js, bin=scripts/cli.js.
+ Purpose: scaffolds a QA methodology (qa/ directory, skills, templates, agent
+ instructions) into consumer projects via `npm install` postinstall hook.
+ Architecture: scripts/ (CLI commands: init, generate, validate, upgrade,
+ sync-version), skills/ (8 domain SKILL.md files copied into consumer
+ projects), templates/ (spec/test-case/defect/instructions templates),
+ integrations/ (optional Playwright, ADO PowerShell, playwright-azure-reporter).
+ Testing: No automated test suite detected in this repo (no test script in
+ package.json, no jest/vitest/mocha config, no *.test.js files at repo scope).
+ Validation is currently manual via `npm run validate` (scripts/validate.js)
+ and the postinstall smoke-path (`--skip-if-exists`).
+ Style: CommonJS scripts, Markdown-first artifacts (SKILL.md, templates),
+ UTF-8 no-BOM encoding required per .github/copilot-instructions.md
+ formatting rules (no em-dash/en-dash/ellipsis/smart quotes/arrows in
+ generated content).
+
+rules:
+ proposal:
+ - Include rollback plan for risky changes (this package is consumed via
+ npm install by other repos; breaking changes to skills/ or scripts/
+ affect all consumer projects on next upgrade).
+ specs:
+ - Use Given/When/Then for scenarios
+ - Use RFC 2119 keywords (MUST, SHALL, SHOULD, MAY)
+ design:
+ - Include sequence diagrams for complex flows
+ - Document architecture decisions with rationale
+ - Consider impact on the install/upgrade flow (postinstall, --skip-if-exists)
+ and on consumer projects' .github/skills for any change to skills/ or scripts/
+ tasks:
+ - Group by phase, use hierarchical numbering
+ - Keep tasks completable in one session
+ apply:
+ - Follow existing code patterns (CommonJS, scripts/ CLI structure)
+ - Preserve UTF-8 no-BOM encoding and the banned-character rules from
+ .github/copilot-instructions.md for any generated content
+ tdd: false # No test runner detected; TDD not enforceable yet
+ test_command: ""
+ verify:
+ test_command: ""
+ build_command: ""
+ coverage_threshold: 0
+ archive:
+ - Warn before merging destructive deltas
diff --git a/openspec/specs/.gitkeep b/openspec/specs/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/package.json b/package.json
index 64f5a7f..4951365 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,9 @@
"init": "node scripts/cli.js init",
"generate": "node scripts/cli.js generate",
"validate": "node scripts/cli.js validate",
- "test": "node --test \"test/**/*.test.js\"",
+ "check-accents": "node scripts/check-spanish-accents.js",
+ "check-chars": "node scripts/check-forbidden-chars.js",
+ "test": "node --test test/*.test.js",
"version": "node scripts/sync-version.js && git add README.md .github/copilot-instructions.md"
},
"files": [
@@ -38,7 +40,7 @@
"qa-framework.config.json"
],
"engines": {
- "node": ">=18"
+ "node": ">=20"
},
"peerDependencies": {
"@keber/ado-qa": ">=1.0.0",
diff --git a/qa-framework.config.json b/qa-framework.config.json
index 3a1e2cc..41437d1 100644
--- a/qa-framework.config.json
+++ b/qa-framework.config.json
@@ -27,7 +27,7 @@
"testCaseIdPattern": "TC-{MODULE}-{SUBMODULE}-{NUM}",
"businessRuleIdPattern": "RN-{MODULE}-{NUM}",
"workflowIdPattern": "FL-{MODULE}-{NUM}",
- "defectIdPattern": "DEF-{NUM}",
+ "defectIdPattern": "DEF-{PREFIX}-{NUM}",
"testNamingPattern": "[{ID}] {title} @{priority}",
"timestampFormat": "YYYY-MM-DD_HH-MM-SS",
"tcPriorityLevels": ["P0", "P1", "P2", "P3"]
@@ -42,6 +42,20 @@
}
],
+ "parallelLanes": {
+ "enabled": false,
+ "lanes": [
+ {
+ "id": "1",
+ "account": "QA_USER",
+ "storageState": ".auth/user-default.json",
+ "mcpNamespace": null,
+ "runnerProject": "chromium",
+ "capabilities": []
+ }
+ ]
+ },
+
"integrations": {
"playwright": {
"enabled": false,
@@ -69,7 +83,14 @@
"variableGroup": "{{ADO_VARIABLE_GROUP}}",
"pipelineFile": "qa/08-azure-integration/pipelines/azure-pipeline-qa.yml",
"moduleRegistry": "qa/08-azure-integration/module-registry.json",
- "reporterPackage": "@alex_neo/playwright-azure-reporter"
+ "reporterPackage": "@alex_neo/playwright-azure-reporter",
+ "sprintCycle": {
+ "enabled": false,
+ "sprintDurationDays": 8,
+ "manualTestingTimeboxDays": 2,
+ "dateFormat": "dd-mm-aaaa",
+ "timezone": "America/Santiago"
+ }
}
},
diff --git a/qa-framework.config.schema.json b/qa-framework.config.schema.json
new file mode 100644
index 0000000..8d30747
--- /dev/null
+++ b/qa-framework.config.schema.json
@@ -0,0 +1,69 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "$id": "https://github.com/keber/qa-framework/qa-framework.config.schema.json",
+ "title": "qa-framework project configuration",
+ "type": "object",
+ "properties": {
+ "frameworkVersion": { "type": "string" },
+ "project": { "type": "object" },
+ "modules": { "type": "array" },
+ "conventions": { "type": "object" },
+ "testUsers": { "type": "array" },
+ "integrations": { "type": "object" },
+ "agentSettings": { "type": "object" },
+
+ "parallelLanes": {
+ "type": "object",
+ "description": "Parallel QA lanes. A lane is a distinct QA account. Lanes exist because the applications under test invalidate a session when the same account logs in again, so two concurrent workers on one account silently destroy each other's session. Omit this block entirely, or set enabled to false, to keep the single-lane behavior a project had before lanes were configurable.",
+ "properties": {
+ "enabled": {
+ "type": "boolean",
+ "description": "When false, the framework behaves exactly as a project with no parallelLanes block: one implicit default lane. Lets a project disable lanes without deleting its lane definitions.",
+ "default": false
+ },
+ "lanes": {
+ "type": "array",
+ "minItems": 1,
+ "description": "The lane table. Each entry couples four values that cannot be inferred from one another.",
+ "items": {
+ "type": "object",
+ "required": ["id", "account", "storageState"],
+ "additionalProperties": false,
+ "properties": {
+ "id": {
+ "type": "string",
+ "pattern": "^[A-Za-z0-9_-]+$",
+ "description": "Lane identifier, as passed in QA_LANE_ONLY and used as a key in the lock state. Restricted to letters, digits, dash and underscore because it travels through shell env and CI variables."
+ },
+ "account": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Env var prefix for this lane's credentials, e.g. QA_USER4 resolves QA_USER4_EMAIL and QA_USER4_PASSWORD."
+ },
+ "storageState": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Playwright storage state file for this lane's account. Must be unique across lanes: two lanes sharing a storageState are not isolated at all."
+ },
+ "mcpNamespace": {
+ "type": ["string", "null"],
+ "description": "Tool prefix when an agent drives this lane over Playwright MCP, e.g. mcp__playwright-qauser4__. Null when this lane has no MCP server provisioned (a CLI-only lane)."
+ },
+ "runnerProject": {
+ "type": ["string", "null"],
+ "description": "Name of the playwright.config.ts project bound to this lane, for CLI and CI runs. Null when the lane is not bound to a runner project."
+ },
+ "capabilities": {
+ "type": "array",
+ "items": { "type": "string", "minLength": 1 },
+ "default": [],
+ "description": "What this lane can actually do, e.g. [\"mcp\"]. Capabilities are PER-LANE state, not a global property of the lane set: a project can have 6 accounts but only 3 MCP servers. Handing a lane to a consumer that needs a capability that lane lacks is a real failure, so consumers declare what they require (lane-lock acquire --require-capability) and the lock only hands out lanes that satisfy it."
+ }
+ }
+ }
+ }
+ },
+ "additionalProperties": false
+ }
+ }
+}
diff --git a/scripts/check-forbidden-chars.js b/scripts/check-forbidden-chars.js
new file mode 100644
index 0000000..59aa924
--- /dev/null
+++ b/scripts/check-forbidden-chars.js
@@ -0,0 +1,180 @@
+#!/usr/bin/env node
+/**
+ * check-forbidden-chars.js
+ *
+ * Enforces the BLOCKING character-safety rule declared in
+ * .github/copilot-instructions.md: generated content must never carry typographic
+ * characters that survive a copy-paste but break on the way to a terminal, a CSV,
+ * a PowerShell script or an Azure DevOps field.
+ *
+ * Forbidden: em-dash (U+2014), en-dash (U+2013), horizontal ellipsis (U+2026),
+ * smart quotes (U+201C, U+201D, U+2018, U+2019) and arrows (U+2190-U+21FF).
+ * Each has an ASCII equivalent that renders identically everywhere.
+ *
+ * Latin Extended (U+00C0-U+024F) is explicitly NOT flagged: Spanish accents,
+ * n with tilde and u/o with umlaut are required orthography, not decoration.
+ * Emoji are not flagged either - they are outside the forbidden set.
+ *
+ * Unlike check-spanish-accents.js this scans .js and .ts as well as .md, because
+ * the generators under scripts/ embed these characters in template literals and
+ * console output that propagate into every consuming project.
+ *
+ * Usage:
+ * node scripts/check-forbidden-chars.js [...] check paths
+ * node scripts/check-forbidden-chars.js --staged check staged files
+ * node scripts/check-forbidden-chars.js --report print findings, never fail
+ *
+ * Exit codes: 0 = clean, 1 = at least one forbidden character, 2 = usage error.
+ */
+
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const { execFileSync } = require('child_process');
+
+// '.example' covers .env.example, which ships to consuming projects like any other
+// template. A BOM or an em-dash there is not cosmetic: some .env parsers read the
+// first key as QA_BASE_URL and silently fail to find it.
+const EXTENSIONS = ['.md', '.js', '.ts', '.ps1', '.example'];
+
+// Code point -> { name, replacement }. Arrows are handled by range below.
+const FORBIDDEN = {
+ 0x2014: { name: 'em dash', replacement: ' - ' },
+ 0x2013: { name: 'en dash', replacement: ' - ' },
+ 0x2026: { name: 'horizontal ellipsis', replacement: '...' },
+ 0x201c: { name: 'left double quotation mark', replacement: '"' },
+ 0x201d: { name: 'right double quotation mark', replacement: '"' },
+ 0x2018: { name: 'left single quotation mark', replacement: "'" },
+ 0x2019: { name: 'right single quotation mark', replacement: "'" },
+};
+
+const ARROW_START = 0x2190;
+const ARROW_END = 0x21ff;
+
+// The two arrows with an obvious ASCII spelling; every other arrow in the block
+// has no single equivalent, so the report asks for a rewrite instead of guessing.
+const ARROW_REPLACEMENTS = {
+ 0x2190: '<-',
+ 0x2192: '->',
+};
+
+function describe(codePoint) {
+ if (FORBIDDEN[codePoint]) return FORBIDDEN[codePoint];
+ if (codePoint >= ARROW_START && codePoint <= ARROW_END) {
+ return {
+ name: 'arrow',
+ replacement: ARROW_REPLACEMENTS[codePoint] || 'an ASCII arrow such as -> or <-',
+ };
+ }
+ return null;
+}
+
+function hex(codePoint) {
+ return 'U+' + codePoint.toString(16).toUpperCase().padStart(4, '0');
+}
+
+/**
+ * Scan one file's text. Returns an array of findings; column is 1-based and
+ * counted in code points, so an astral character earlier on the line does not
+ * shift the reported position.
+ */
+function scanText(text, file) {
+ const findings = [];
+ const lines = text.split(/\r?\n/);
+
+ lines.forEach((line, lineIndex) => {
+ let column = 0;
+ for (const char of line) {
+ column += 1;
+ const codePoint = char.codePointAt(0);
+ const match = describe(codePoint);
+ if (!match) continue;
+ findings.push({
+ file,
+ line: lineIndex + 1,
+ column,
+ char,
+ codePoint,
+ name: match.name,
+ replacement: match.replacement,
+ text: line.trim(),
+ });
+ }
+ });
+
+ return findings;
+}
+
+function scanFile(file) {
+ return scanText(fs.readFileSync(file, 'utf8'), file);
+}
+
+function collect(target) {
+ const stat = fs.statSync(target);
+ if (stat.isFile()) return EXTENSIONS.includes(path.extname(target)) ? [target] : [];
+ return fs.readdirSync(target).flatMap((entry) => {
+ if (entry === 'node_modules' || entry === '.git') return [];
+ return collect(path.join(target, entry));
+ });
+}
+
+function stagedFiles() {
+ const out = execFileSync(
+ 'git',
+ ['diff', '--cached', '--name-only', '--diff-filter=ACM'],
+ { encoding: 'utf8' }
+ );
+ return out
+ .split('\n')
+ .map((s) => s.trim())
+ .filter((s) => s && EXTENSIONS.includes(path.extname(s)) && fs.existsSync(s));
+}
+
+function main(argv) {
+ const reportOnly = argv.includes('--report');
+ const args = argv.filter((a) => a !== '--report');
+
+ let files;
+ if (args.includes('--staged')) {
+ files = stagedFiles();
+ } else if (args.length) {
+ files = args.flatMap(collect);
+ } else {
+ console.error('usage: check-forbidden-chars.js ... | --staged [--report]');
+ return 2;
+ }
+
+ const findings = files.flatMap(scanFile);
+
+ if (findings.length) {
+ const stream = reportOnly ? console.log : console.error;
+ stream(`\nForbidden characters (${findings.length} occurrence(s)):\n`);
+ for (const f of findings) {
+ stream(` ${f.file}:${f.line}:${f.column} "${f.char}" ${hex(f.codePoint)} ${f.name}`);
+ stream(` use ${f.replacement}`);
+ stream(` ${f.text}`);
+ }
+ if (!reportOnly) {
+ stream(
+ '\nThese characters break on the way to a terminal, a CSV export or an Azure' +
+ '\nDevOps field. Replace them with their ASCII equivalents rather than' +
+ '\nbypassing this check. Spanish accents are permitted and are never flagged.\n'
+ );
+ return 1;
+ }
+ return 0;
+ }
+
+ if (reportOnly) {
+ console.log(`check-forbidden-chars: ${files.length} file(s) checked, no findings.`);
+ return 0;
+ }
+
+ console.log(`check-forbidden-chars: ${files.length} file(s) checked, no violations.`);
+ return 0;
+}
+
+if (require.main === module) process.exit(main(process.argv.slice(2)));
+
+module.exports = { scanText, scanFile, main, EXTENSIONS, FORBIDDEN, ARROW_START, ARROW_END };
diff --git a/scripts/check-spanish-accents.js b/scripts/check-spanish-accents.js
new file mode 100644
index 0000000..6d3a5ba
--- /dev/null
+++ b/scripts/check-spanish-accents.js
@@ -0,0 +1,159 @@
+#!/usr/bin/env node
+/**
+ * check-spanish-accents.js
+ *
+ * Fails when a Markdown file whose prose is Spanish carries no accented characters.
+ *
+ * Why this exists: a spec written as "Codigo" while the UI renders "Código" produces a
+ * page-object selector that never matches. In one project that cost 24 of 39 failing smoke
+ * tests and two wrong mitigations (raising a timeout, forcing HTTP/1.1) before the real
+ * cause was found, because the failure presents as a timeout, not as a text mismatch.
+ * Accent stripping is a defect, not a house style - and its measured incidence in
+ * agent-generated artifacts is high enough that honor-system compliance does not hold.
+ *
+ * The hard part is not detecting missing accents; it is deciding which files are Spanish.
+ * A naive "every .md must contain accents" rule fails every legitimately English artifact
+ * the framework ships. Counting Spanish marker words in raw text does not separate them
+ * either: an English document about a Spanish-language project quotes enough Spanish paths,
+ * identifiers and snippets to cross any absolute threshold that still catches short
+ * Spanish files.
+ *
+ * So this classifies on the density of Spanish function words in PROSE ONLY, after
+ * stripping fenced code, inline code, link targets, HTML tags and table pipes. English
+ * prose that merely cites Spanish identifiers scores low because those citations live in
+ * the stripped regions; real Spanish prose scores high because its function words are
+ * spread through the sentences themselves.
+ *
+ * Usage:
+ * node scripts/check-spanish-accents.js [...] check paths
+ * node scripts/check-spanish-accents.js --staged check staged .md files
+ * node scripts/check-spanish-accents.js --report print scores, never fail
+ *
+ * Exit codes: 0 = clean, 1 = at least one Spanish file has no accents, 2 = usage error.
+ */
+
+'use strict';
+
+const fs = require('fs');
+const path = require('path');
+const { execFileSync } = require('child_process');
+
+// Function words that are unavoidable in real Spanish prose and rare in English text.
+// Deliberately excludes "no", "a", "en", "es" and other tokens that collide with English
+// or with code, and excludes accented forms so a stripped file is not penalised twice.
+const MARKERS = [
+ 'que', 'para', 'con', 'por', 'los', 'las', 'del', 'una', 'como', 'pero',
+ 'este', 'esta', 'estos', 'estas', 'sin', 'sobre', 'desde', 'cuando', 'donde',
+ 'cada', 'todo', 'todos', 'toda', 'todas', 'entre', 'hasta', 'porque', 'segun',
+ 'debe', 'deben', 'ser', 'son', 'hay', 'muy', 'ya', 'si', 'lo', 'le', 'se',
+];
+
+const ACCENTED = /[áéíóúÁÉÍÓÚñÑüÜ]/;
+
+// A file is Spanish when at least this share of its prose words are Spanish markers.
+// Calibrated against this package's own corpus: Spanish agent templates land near 0.13,
+// English skill files and the FRAMEWORK-FIXES reports stay below 0.03.
+const DENSITY_THRESHOLD = 0.07;
+// Below this many prose words the density estimate is too noisy to act on.
+const MIN_PROSE_WORDS = 60;
+
+/** Remove every region where Spanish tokens are citations rather than prose. */
+function extractProse(markdown) {
+ return markdown
+ .replace(/^---\n[\s\S]*?\n---/, ' ') // YAML frontmatter
+ .replace(/```[\s\S]*?```/g, ' ') // fenced code
+ .replace(/~~~[\s\S]*?~~~/g, ' ') // fenced code, alt syntax
+ .replace(/`[^`\n]*`/g, ' ') // inline code
+ .replace(/\]\([^)]*\)/g, '] ') // link targets, keep link text
+ .replace(/<[^>\n]*>/g, ' ') // HTML tags and
+ .replace(/\{\{[^}]*\}\}/g, ' ') // {{TEMPLATE_PLACEHOLDERS}}
+ .replace(/\|/g, ' ') // table pipes
+ .replace(/^\s{4,}\S.*$/gm, ' ') // indented code blocks
+ .replace(/[A-Za-z0-9_.\-]+\/[A-Za-z0-9_./\-]+/g, ' ') // bare paths
+ .replace(/[#*_>[\]]/g, ' '); // remaining markdown punctuation
+}
+
+function score(markdown) {
+ const prose = extractProse(markdown);
+ const words = prose.toLowerCase().match(/[a-zà-ÿ]+/g) || [];
+ const markers = words.filter((w) => MARKERS.includes(w)).length;
+ const density = words.length ? markers / words.length : 0;
+ return {
+ words: words.length,
+ markers,
+ density,
+ isSpanish: words.length >= MIN_PROSE_WORDS && density >= DENSITY_THRESHOLD,
+ hasAccents: ACCENTED.test(markdown),
+ };
+}
+
+function collect(target) {
+ const stat = fs.statSync(target);
+ if (stat.isFile()) return target.endsWith('.md') ? [target] : [];
+ return fs.readdirSync(target).flatMap((entry) => {
+ if (entry === 'node_modules' || entry === '.git') return [];
+ return collect(path.join(target, entry));
+ });
+}
+
+function stagedMarkdown() {
+ const out = execFileSync(
+ 'git',
+ ['diff', '--cached', '--name-only', '--diff-filter=ACM', '--', '*.md'],
+ { encoding: 'utf8' }
+ );
+ return out.split('\n').map((s) => s.trim()).filter((s) => s && fs.existsSync(s));
+}
+
+function main(argv) {
+ const reportOnly = argv.includes('--report');
+ const args = argv.filter((a) => a !== '--report');
+
+ let files;
+ if (args.includes('--staged')) {
+ files = stagedMarkdown();
+ } else if (args.length) {
+ files = args.flatMap(collect);
+ } else {
+ console.error('usage: check-spanish-accents.js ... | --staged [--report]');
+ return 2;
+ }
+
+ const failures = [];
+ for (const file of files) {
+ const result = score(fs.readFileSync(file, 'utf8'));
+ if (reportOnly) {
+ console.log(
+ `${result.isSpanish ? 'ES' : 'en'} density=${result.density.toFixed(3)} ` +
+ `words=${String(result.words).padStart(5)} accents=${result.hasAccents ? 'yes' : 'NO '} ${file}`
+ );
+ }
+ if (result.isSpanish && !result.hasAccents) failures.push({ file, ...result });
+ }
+
+ if (reportOnly) return 0;
+
+ if (failures.length) {
+ console.error('\nSpanish Markdown without accented characters:\n');
+ for (const f of failures) {
+ console.error(` ${f.file}`);
+ console.error(
+ ` ${f.markers} Spanish markers in ${f.words} prose words ` +
+ `(density ${f.density.toFixed(3)}), 0 accented characters.`
+ );
+ }
+ console.error(
+ '\nSpanish prose must carry its accents: a spec saying "Codigo" does not match a UI' +
+ '\nthat renders "Código", and the resulting selector failure looks like a timeout.' +
+ '\nFix the orthography rather than bypassing this check.\n'
+ );
+ return 1;
+ }
+
+ console.log(`check-spanish-accents: ${files.length} file(s) checked, no violations.`);
+ return 0;
+}
+
+if (require.main === module) process.exit(main(process.argv.slice(2)));
+
+module.exports = { score, extractProse, MARKERS, DENSITY_THRESHOLD };
diff --git a/scripts/cli.js b/scripts/cli.js
index 23c0d92..984c161 100644
--- a/scripts/cli.js
+++ b/scripts/cli.js
@@ -1,6 +1,6 @@
#!/usr/bin/env node
/**
- * scripts/cli.js — keber/qa-framework CLI entry point
+ * scripts/cli.js - keber/qa-framework CLI entry point
*
* Usage:
* npx keber/qa-framework [options]
diff --git a/scripts/generate.js b/scripts/generate.js
index 66cec0e..6ce42ff 100644
--- a/scripts/generate.js
+++ b/scripts/generate.js
@@ -1,6 +1,6 @@
#!/usr/bin/env node
/**
- * scripts/generate.js — Generate a qa artifact from a template
+ * scripts/generate.js - Generate a qa artifact from a template
*
* Usage:
* qa-framework generate [options]
@@ -39,7 +39,7 @@ const outputDir = getArg('--output') ? path.resolve(process.cwd(), getArg
const templateDir = path.resolve(__dirname, '..', 'templates');
const ARTIFACTS = {
- spec: null, // special — copies all 6 files
+ spec: null, // special - copies all 6 files
'test-plan': path.join(templateDir, 'test-plan.md'),
'test-case': path.join(templateDir, 'test-case.md'),
'execution-report': path.join(templateDir, 'execution-report.md'),
diff --git a/scripts/init.js b/scripts/init.js
index d714c82..6d2ad3c 100644
--- a/scripts/init.js
+++ b/scripts/init.js
@@ -15,6 +15,9 @@
const fs = require('fs');
const path = require('path');
+const { buildCommandContent, discoverSkillNames, commandFileName } = require('./lib/claude-commands');
+const { AGENT_NAMES, isSprintCycleEnabled, buildAgentContent, agentFileName } = require('./lib/claude-agents');
+
// --- Parse args ---
const args = process.argv.slice(2);
const configFlag = args.indexOf('--config');
@@ -126,7 +129,7 @@ if (fs.existsSync(qaReadmeTemplate)) {
// --- qa/memory/INDEX.md ---
const memoryDir = path.join(qaRoot, 'memory');
writeIfMissing(path.join(memoryDir, 'INDEX.md'),
-`# Memory Index — ${config.project?.displayName ?? config.project?.name ?? 'Project'}
+`# Memory Index - ${config.project?.displayName ?? config.project?.name ?? 'Project'}
> The agent reads this file first before loading any memory file.
> Add a row here whenever you create or update a file in this directory.
@@ -146,7 +149,7 @@ for (const file of ['ci-pipeline-findings.md', 'e2e-stabilization-patterns.md',
// --- 03-test-cases README (optional directory marker) ---
writeIfMissing(path.join(qaRoot, '03-test-cases', 'README.md'),
-`# 03-test-cases/ — Optional Standalone Test Cases
+`# 03-test-cases/ - Optional Standalone Test Cases
> **v1.7.0+:** The primary location for test cases (with detailed steps) is now
> \`qa/02-test-plans/sprints/Sprint-{N}/Plan-de-Pruebas-{project}-Sprint-{N}-{module}.md\`.
@@ -198,7 +201,7 @@ for (const mod of modules) {
for (const sub of submodules) {
const subKey = sub.key ?? sub.name.toLowerCase().replace(/\s+/g, '-');
- const subDir = path.join(qaRoot, moduleKey, subKey);
+ const subDir = path.join(qaRoot, '01-specifications', moduleKey, subKey);
fs.mkdirSync(subDir, { recursive: true });
for (const specFile of SPEC_FILES) {
@@ -245,6 +248,19 @@ for (const file of ['playwright.config.ts', 'global-setup.ts', '.env.example', '
console.log(` [created] ${path.relative(cwd, dest)}`);
}
}
+// Lane scripts: the lane lock plus its config reader and global-setup guards. Plain
+// CommonJS so they run as a CLI and stay testable with `node --test` without pulling
+// in a TypeScript test runner.
+const laneScriptsDir = path.join(e2eScaffoldDir, 'scripts');
+fs.mkdirSync(laneScriptsDir, { recursive: true });
+for (const file of ['lane-config.js', 'lane-lock.js', 'global-setup-guards.js']) {
+ const dest = path.join(laneScriptsDir, file);
+ if (!fs.existsSync(dest)) {
+ fs.copyFileSync(path.join(scaffoldSrc, 'scripts', file), dest);
+ console.log(` [created] ${path.relative(cwd, dest)}`);
+ }
+}
+
const fixturesDir = path.join(e2eScaffoldDir, 'fixtures');
fs.mkdirSync(fixturesDir, { recursive: true });
for (const file of ['auth.ts', 'test-helpers.ts', 'base.ts']) {
@@ -283,7 +299,7 @@ const adoDir = path.join(qaRoot, '08-azure-integration');
writeIfMissing(path.join(adoDir, 'README.md'), `# ADO Integration\n\nSee keber/qa-framework integrations/ado-powershell/ for setup instructions.\n`);
writeIfMissing(path.join(adoDir, 'module-registry.json'), JSON.stringify({ modules: [] }, null, 2));
-// --- Skills → .github/skills/ ---
+// --- Skills -> .github/skills/ ---
const skillsSrc = path.resolve(__dirname, '..', 'skills');
const skillsDest = path.join(cwd, '.github', 'skills');
fs.mkdirSync(skillsDest, { recursive: true });
@@ -298,7 +314,7 @@ if (fs.existsSync(skillsSrc)) {
}
}
-// --- QA structure guide → qa/QA-STRUCTURE-GUIDE.md ---
+// --- QA structure guide -> qa/QA-STRUCTURE-GUIDE.md ---
const structureGuideSrc = path.resolve(__dirname, '..', 'docs', 'folder-structure-guide.md');
const structureGuideDest = path.join(qaRoot, 'QA-STRUCTURE-GUIDE.md');
if (fs.existsSync(structureGuideSrc)) {
@@ -314,7 +330,33 @@ const copilotInstrContent = fs.readFileSync(instrTemplatePath, 'utf8')
.replace('{{VERSION}}', config.frameworkVersion ?? '1.0.0');
writeIfMissing(copilotInstrPath, copilotInstrContent);
-// --- AGENT-NEXT-STEPS.md — readable by the agent after install ---
+// --- .claude/commands/qa-*.md - thin wrappers pointing at .github/skills/ ---
+const claudeCommandsDest = path.join(cwd, '.claude', 'commands');
+fs.mkdirSync(claudeCommandsDest, { recursive: true });
+for (const skillName of discoverSkillNames(skillsSrc)) {
+ writeIfMissing(path.join(claudeCommandsDest, commandFileName(skillName)), buildCommandContent(skillName));
+}
+
+// --- .claude/rules/qa-framework.md - always-loaded rules (Claude Code equivalent of applyTo: '**') ---
+const claudeRulesPath = path.join(cwd, '.claude', 'rules', 'qa-framework.md');
+const rulesTemplatePath = path.resolve(__dirname, '..', 'templates', 'qa-framework.rules.md');
+const claudeRulesContent = fs.readFileSync(rulesTemplatePath, 'utf8')
+ .replace('{{VERSION}}', config.frameworkVersion ?? '1.0.0');
+writeIfMissing(claudeRulesPath, claudeRulesContent);
+
+// --- .claude/agents/qa-*.md - optional ANALISIS/PLAN sprint-cycle mode (Claude Code only) ---
+// Gated by integrations.azureDevOps.sprintCycle.enabled. No .github/ equivalent: Claude
+// Code subagents with per-agent tools/model frontmatter have no Copilot counterpart.
+if (isSprintCycleEnabled(config)) {
+ const templatesDir = path.resolve(__dirname, '..', 'templates');
+ const claudeAgentsDest = path.join(cwd, '.claude', 'agents');
+ fs.mkdirSync(claudeAgentsDest, { recursive: true });
+ for (const agentName of AGENT_NAMES) {
+ writeIfMissing(path.join(claudeAgentsDest, agentFileName(agentName)), buildAgentContent(agentName, templatesDir, config));
+ }
+}
+
+// --- AGENT-NEXT-STEPS.md - readable by the agent after install ---
const nextStepsContent = `# ✅ @keber/qa-framework installed successfully
> This file was generated automatically by the postinstall script.
@@ -324,6 +366,7 @@ const nextStepsContent = `# ✅ @keber/qa-framework installed successfully
- \`qa/\` folder structure with spec templates and agent instructions
- \`.github/instructions/qa-framework.instructions.md\` with QA agent behavior rules (framework-owned, safe to upgrade)
+- \`.claude/rules/qa-framework.md\` and \`.claude/commands/qa-*.md\` - equivalent Claude Code artifacts (framework-owned, safe to upgrade)
## Required next steps
@@ -362,12 +405,17 @@ const adoQaInstalledFinal = fs.existsSync(path.join(cwd, 'node_modules', '@keber
const azureReporterInstalled = fs.existsSync(path.join(cwd, 'node_modules', '@alex_neo', 'playwright-azure-reporter'));
console.log('');
-console.log(' @keber/qa-framework — scaffold complete');
+console.log(' @keber/qa-framework - scaffold complete');
console.log(' ----------------------------------------');
console.log(' Installed:');
console.log(' qa/ QA directory structure + spec templates');
console.log(' .github/skills/ QA agent skills (8 stages)');
console.log(' .github/instructions/ qa-framework.instructions.md');
+console.log(' .claude/commands/ qa-*.md (Claude Code slash commands)');
+console.log(' .claude/rules/ qa-framework.md (Claude Code always-loaded rules)');
+if (isSprintCycleEnabled(config)) {
+ console.log(' .claude/agents/ qa-analisis/qa-plan/qa-asesoria/qa-informe-resultados.md (ANALISIS/PLAN mode)');
+}
console.log('');
console.log(' Optional integrations:');
if (playwrightInstalled) {
diff --git a/scripts/lib/claude-agents.js b/scripts/lib/claude-agents.js
new file mode 100644
index 0000000..f4fce08
--- /dev/null
+++ b/scripts/lib/claude-agents.js
@@ -0,0 +1,78 @@
+'use strict';
+
+/**
+ * scripts/lib/claude-agents.js - Generate .claude/agents/qa-*.md sprint-cycle subagents
+ *
+ * Optional ANALISIS/PLAN mode, gated by integrations.azureDevOps.sprintCycle.enabled in
+ * qa-framework.config.json. Claude Code only - subagents with per-agent tools/model
+ * frontmatter have no GitHub Copilot equivalent, so no .github/ counterpart is generated.
+ *
+ * Placeholders are resolved from config.integrations.azureDevOps.sprintCycle, falling back
+ * to neutral defaults when a field (or the whole config section) is absent.
+ */
+
+const fs = require('fs');
+const path = require('path');
+
+const AGENT_NAMES = ['qa-analisis', 'qa-plan', 'qa-asesoria', 'qa-informe-resultados'];
+
+const DEFAULT_SPRINT_CYCLE = {
+ sprintDurationDays: 8,
+ manualTestingTimeboxDays: 2,
+ dateFormat: 'dd-mm-aaaa',
+ timezone: 'America/Santiago',
+};
+
+const LOCALE_LANGUAGE_LABELS = {
+ es: 'espanol',
+ 'es-cl': 'espanol de Chile',
+ 'es-mx': 'espanol de Mexico',
+ 'es-ar': 'espanol de Argentina',
+ en: 'English',
+};
+
+function isSprintCycleEnabled(config) {
+ return Boolean(config?.integrations?.azureDevOps?.sprintCycle?.enabled);
+}
+
+function resolveLocaleLanguageLabel(locale) {
+ if (!locale) return 'espanol';
+ return LOCALE_LANGUAGE_LABELS[locale.toLowerCase()] ?? locale;
+}
+
+function buildPlaceholders(config) {
+ const sprintCycle = { ...DEFAULT_SPRINT_CYCLE, ...(config?.integrations?.azureDevOps?.sprintCycle ?? {}) };
+ const project = config?.project ?? {};
+
+ return {
+ '{{PROJECT_NAME}}': project.name ?? '{{PROJECT_NAME}}',
+ '{{PROJECT_DISPLAY_NAME}}': project.displayName ?? project.name ?? '{{PROJECT_DISPLAY_NAME}}',
+ '{{SPRINT_DURATION_DAYS}}': String(sprintCycle.sprintDurationDays),
+ '{{MANUAL_TESTING_TIMEBOX_DAYS}}': String(sprintCycle.manualTestingTimeboxDays),
+ '{{DATE_FORMAT}}': sprintCycle.dateFormat,
+ '{{TIMEZONE}}': sprintCycle.timezone,
+ '{{LOCALE_LANGUAGE_LABEL}}': resolveLocaleLanguageLabel(config?.conventions?.locale ?? config?.conventions?.language),
+ '{{DEFECT_ID_PREFIX}}': config?.project?.name ? config.project.name.toUpperCase().replace(/[^A-Z0-9]/g, '-') : 'PROJ',
+ };
+}
+
+function buildAgentContent(agentName, templatesDir, config) {
+ const templatePath = path.join(templatesDir, 'agents', `${agentName}.md`);
+ let content = fs.readFileSync(templatePath, 'utf8');
+ const placeholders = buildPlaceholders(config);
+ for (const [placeholder, value] of Object.entries(placeholders)) {
+ content = content.split(placeholder).join(value);
+ }
+ return content;
+}
+
+function agentFileName(agentName) {
+ return `${agentName}.md`;
+}
+
+module.exports = {
+ AGENT_NAMES,
+ isSprintCycleEnabled,
+ buildAgentContent,
+ agentFileName,
+};
diff --git a/scripts/lib/claude-commands.js b/scripts/lib/claude-commands.js
new file mode 100644
index 0000000..817f2a1
--- /dev/null
+++ b/scripts/lib/claude-commands.js
@@ -0,0 +1,56 @@
+'use strict';
+
+/**
+ * scripts/lib/claude-commands.js - Generate .claude/commands/qa-{name}.md thin wrappers
+ *
+ * Each generated command points back at the corresponding SKILL.md as the single
+ * source of truth (no content duplication, no drift). Skills are discovered
+ * dynamically from the filesystem, never hardcoded, so a newly added skill
+ * under skills/ is picked up automatically by init.js and upgrade.js.
+ */
+
+const fs = require('fs');
+const path = require('path');
+
+// Stage prerequisites mirrored from templates/qa-framework.instructions.md pipeline table.
+// Skills not listed here (e.g. a future addition) still get a valid command with no
+// prerequisite line, and this codebase does not need updating for the command to work.
+const STAGE_PREREQUISITES = {
+ 'qa-module-analysis': 'Prerequisite: None - this is the first stage of the QA pipeline.',
+ 'qa-spec-generation': 'Prerequisite: `00-inventory.md` exists.',
+ 'qa-test-plan': 'Prerequisite: `05-test-scenarios.md` exists.',
+ 'qa-test-cases': 'Prerequisite: Test plan exists.',
+ 'qa-automation': 'Prerequisite: Specs approved, no PENDING-CODE.',
+ 'qa-test-stabilization': 'Prerequisite: Failing or flaky tests exist in `qa/07-automation/e2e/`.',
+ 'qa-maintenance': 'Prerequisite: Application change delivered.',
+ 'qa-ado-integration': 'Prerequisite: ADO enabled in `qa/qa-framework.config.json`.',
+};
+
+function buildCommandContent(skillName) {
+ const prerequisite = STAGE_PREREQUISITES[skillName];
+ const lines = [
+ `Read the full skill at \`.github/skills/${skillName}/SKILL.md\` FIRST, then follow its instructions exactly.`,
+ ];
+ if (prerequisite) {
+ lines.push('', prerequisite);
+ }
+ return `${lines.join('\n')}\n`;
+}
+
+function discoverSkillNames(skillsSrcDir) {
+ if (!fs.existsSync(skillsSrcDir)) return [];
+ return fs.readdirSync(skillsSrcDir, { withFileTypes: true })
+ .filter((entry) => entry.isDirectory())
+ .map((entry) => entry.name)
+ .sort();
+}
+
+function commandFileName(skillName) {
+ return `${skillName}.md`;
+}
+
+module.exports = {
+ buildCommandContent,
+ discoverSkillNames,
+ commandFileName,
+};
diff --git a/scripts/sync-version.js b/scripts/sync-version.js
index c631af2..2224085 100644
--- a/scripts/sync-version.js
+++ b/scripts/sync-version.js
@@ -13,6 +13,11 @@ const targets = [
path.join(rootDir, '.github', 'copilot-instructions.md'),
];
+// templates/qa-framework.instructions.md and templates/qa-framework.rules.md contain a
+// literal {{VERSION}} placeholder substituted by init.js/upgrade.js in the consumer
+// project at install/upgrade time. That placeholder is NOT a hardcoded version string
+// and must never be touched here.
+
const versionPattern = /v\d+\.\d+\.\d+/;
for (const filePath of targets) {
diff --git a/scripts/upgrade.js b/scripts/upgrade.js
index f100d38..7794883 100644
--- a/scripts/upgrade.js
+++ b/scripts/upgrade.js
@@ -1,6 +1,6 @@
#!/usr/bin/env node
/**
- * scripts/upgrade.js — Upgrade framework-owned files in an existing project
+ * scripts/upgrade.js - Upgrade framework-owned files in an existing project
*
* Usage:
* qa-framework upgrade
@@ -46,6 +46,9 @@
const fs = require('fs');
const path = require('path');
+const { buildCommandContent, discoverSkillNames, commandFileName } = require('./lib/claude-commands');
+const { AGENT_NAMES, isSprintCycleEnabled, buildAgentContent, agentFileName } = require('./lib/claude-agents');
+
const args = process.argv.slice(2);
const dryRun = args.includes('--dry-run');
@@ -78,17 +81,17 @@ console.log(`\n[qa-framework/upgrade] ${dryRun ? '(dry-run) ' : ''}Upgrading fra
console.log(`[qa-framework/upgrade] Project root: ${cwd}\n`);
// ---------------------------------------------------------------------------
-// 1. .github/skills/ — always overwrite (framework-owned)
+// 1. .github/skills/ - always overwrite (framework-owned)
// ---------------------------------------------------------------------------
if (!fs.existsSync(skillsSrc)) {
- warnings.push('skills/ source directory not found in package — skipping skill install');
+ warnings.push('skills/ source directory not found in package - skipping skill install');
} else {
fs.mkdirSync(skillsDest, { recursive: true });
copyDirForce(skillsSrc, skillsDest);
}
// ---------------------------------------------------------------------------
-// 2. .github/instructions/qa-framework.instructions.md — overwrite (framework-owned)
+// 2. .github/instructions/qa-framework.instructions.md - overwrite (framework-owned)
// ---------------------------------------------------------------------------
const copilotInstrPath = path.join(githubDir, 'instructions', 'qa-framework.instructions.md');
const instrTemplatePath = path.resolve(__dirname, '..', 'templates', 'qa-framework.instructions.md');
@@ -96,13 +99,40 @@ const copilotContent = fs.readFileSync(instrTemplatePath, 'utf8')
.replace('{{VERSION}}', config.frameworkVersion ?? '1.0.0');
forceWrite(copilotInstrPath, copilotContent);
+// ---------------------------------------------------------------------------
+// 2a. .claude/commands/qa-*.md and .claude/rules/qa-framework.md - overwrite (framework-owned)
+// ---------------------------------------------------------------------------
+const claudeCommandsDest = path.join(cwd, '.claude', 'commands');
+for (const skillName of discoverSkillNames(skillsSrc)) {
+ forceWrite(path.join(claudeCommandsDest, commandFileName(skillName)), buildCommandContent(skillName));
+}
+
+const claudeRulesPath = path.join(cwd, '.claude', 'rules', 'qa-framework.md');
+const rulesTemplatePath = path.resolve(__dirname, '..', 'templates', 'qa-framework.rules.md');
+const claudeRulesContent = fs.readFileSync(rulesTemplatePath, 'utf8')
+ .replace('{{VERSION}}', config.frameworkVersion ?? '1.0.0');
+forceWrite(claudeRulesPath, claudeRulesContent);
+
+// ---------------------------------------------------------------------------
+// 2c. .claude/agents/qa-*.md - optional ANALISIS/PLAN sprint-cycle mode (Claude Code only)
+// Gated by integrations.azureDevOps.sprintCycle.enabled. Framework-owned when the
+// flag is on; never generated (and never deleted if it was hand-authored) otherwise.
+// ---------------------------------------------------------------------------
+if (isSprintCycleEnabled(config)) {
+ const templatesDir = path.resolve(__dirname, '..', 'templates');
+ const claudeAgentsDest = path.join(cwd, '.claude', 'agents');
+ for (const agentName of AGENT_NAMES) {
+ forceWrite(path.join(claudeAgentsDest, agentFileName(agentName)), buildAgentContent(agentName, templatesDir, config));
+ }
+}
+
// ---------------------------------------------------------------------------
// 2b. Migration: strip QA Framework section from old copilot-instructions.md
//
// Handles all cases:
-// A. File has custom instructions + QA section → keep custom, strip QA section
-// B. File has ONLY QA section (any version) → delete the file
-// C. File does not mention QA Framework → leave untouched
+// A. File has custom instructions + QA section -> keep custom, strip QA section
+// B. File has ONLY QA section (any version) -> delete the file
+// C. File does not mention QA Framework -> leave untouched
//
// The QA section always starts with "# QA Framework Instructions" across all
// previous versions, so that heading is the reliable split point.
@@ -118,22 +148,22 @@ if (fs.existsSync(oldCopilotPath)) {
const before = oldContent.slice(0, qaIdx).replace(/\s*\n---\s*$/, '').trim();
if (before.length === 0) {
- // Case B: file contained only QA Framework content → delete it
+ // Case B: file contained only QA Framework content -> delete it
if (!dryRun) fs.unlinkSync(oldCopilotPath);
updated.push(oldCopilotPath);
- console.log(` [deleted] .github/copilot-instructions.md (contained only QA Framework rules — now in .github/instructions/qa-framework.instructions.md)`);
+ console.log(` [deleted] .github/copilot-instructions.md (contained only QA Framework rules - now in .github/instructions/qa-framework.instructions.md)`);
} else {
- // Case A: file had custom content too → write back only the custom part
+ // Case A: file had custom content too -> write back only the custom part
if (!dryRun) fs.writeFileSync(oldCopilotPath, before + '\n', 'utf8');
updated.push(oldCopilotPath);
- console.log(` [cleaned] .github/copilot-instructions.md — removed QA Framework section, kept custom instructions`);
+ console.log(` [cleaned] .github/copilot-instructions.md - removed QA Framework section, kept custom instructions`);
}
}
- // Case C: no QA marker found → leave untouched (no log noise)
+ // Case C: no QA marker found -> leave untouched (no log noise)
}
// ---------------------------------------------------------------------------
-// 3. qa/QA-STRUCTURE-GUIDE.md — overwrite (framework doc)
+// 3. qa/QA-STRUCTURE-GUIDE.md - overwrite (framework doc)
// ---------------------------------------------------------------------------
const structureGuideSrc = path.resolve(__dirname, '..', 'docs', 'folder-structure-guide.md');
const structureGuideDest = path.join(qaRoot, 'QA-STRUCTURE-GUIDE.md');
@@ -220,7 +250,7 @@ if (fs.existsSync(e2eDir)) {
console.log(` [migrated] e2e/${entry.name}/ -> e2e/tests/${entry.name}/`);
} else {
warnings.push(
- `Cannot migrate e2e/${entry.name}/ — target e2e/tests/${entry.name}/ already exists. Merge manually.`
+ `Cannot migrate e2e/${entry.name}/ - target e2e/tests/${entry.name}/ already exists. Merge manually.`
);
}
}
@@ -275,7 +305,7 @@ if (fs.existsSync(playwrightConfigPath)) {
if (/testDir\s*:\s*['"]\.['"]/.test(cfg)) {
cfg = cfg.replace(/testDir\s*:\s*['"]\.['"]/g, "testDir: './tests'");
cfgChanged = true;
- console.log(` [patched] e2e/playwright.config.ts — testDir: '.' -> './tests'`);
+ console.log(` [patched] e2e/playwright.config.ts - testDir: '.' -> './tests'`);
}
// Inject testIgnore after testDir line if not present
@@ -285,7 +315,7 @@ if (fs.existsSync(playwrightConfigPath)) {
"$1 testIgnore: ['**/helpers/debug/**', '**/seeds/**'],\n"
);
cfgChanged = true;
- console.log(` [patched] e2e/playwright.config.ts — added testIgnore`);
+ console.log(` [patched] e2e/playwright.config.ts - added testIgnore`);
}
if (cfgChanged) {
@@ -344,7 +374,7 @@ if (fs.existsSync(testPlansDir)) {
updated.push(newFile);
console.log(` [migrated] 02-test-plans/${entry.name} -> 02-test-plans/sprints/legacy/${entry.name}`);
} else {
- warnings.push(`Cannot migrate 02-test-plans/${entry.name} — target already exists. Move manually.`);
+ warnings.push(`Cannot migrate 02-test-plans/${entry.name} - target already exists. Move manually.`);
}
}
}
@@ -363,7 +393,7 @@ if (!fs.existsSync(testCasesReadme)) {
if (!dryRun) {
fs.mkdirSync(path.dirname(testCasesReadme), { recursive: true });
fs.writeFileSync(testCasesReadme,
- '# 03-test-cases/ — Optional Standalone Test Cases\n\n' +
+ '# 03-test-cases/ - Optional Standalone Test Cases\n\n' +
'> **v1.7.0+:** The primary location for test cases (with detailed steps) is now\n' +
'> `qa/02-test-plans/sprints/Sprint-{N}/Plan-de-Pruebas-{project}-Sprint-{N}-{module}.md`.\n' +
'>\n' +
@@ -451,7 +481,7 @@ if (updated.length) {
}
if (skipped.length) {
console.log('\n Skipped (already up to date):');
- for (const f of skipped) console.log(` — ${path.relative(cwd, f)}`);
+ for (const f of skipped) console.log(` - ${path.relative(cwd, f)}`);
}
if (warnings.length) {
console.log('\n Warnings:');
diff --git a/scripts/validate.js b/scripts/validate.js
index b014f17..3fdd2d1 100644
--- a/scripts/validate.js
+++ b/scripts/validate.js
@@ -1,6 +1,6 @@
#!/usr/bin/env node
/**
- * scripts/validate.js — Validate qa/ folder structure and conventions
+ * scripts/validate.js - Validate qa/ folder structure and conventions
*
* Usage:
* qa-framework validate
@@ -9,7 +9,7 @@
*
* Checks:
* 1. Required top-level folders exist
- * 2. All submodule folders contain the 6 required spec files
+ * 2. All submodule folders under 01-specifications/ contain the 6 required spec files
* 3. No plaintext credentials in spec files (basic scan)
* 4. Test case naming convention (TC-NNN pattern in spec files)
* 5. [--strict] Automation spec files exist for every submodule
@@ -70,18 +70,18 @@ const SPEC_FILES = [
'05-test-scenarios.md',
];
-const SKIP_DIRS = new Set([
- '00-guides', '00-standards', '05-test-execution',
- '06-defects', '07-automation', '08-azure-integration',
-]);
+// Specs live at qa/01-specifications/{module}/{submodule}/. Rooting the scan
+// here keeps it at the right depth and means sibling folders such as
+// 02-test-plans/ or memory/ are never mistaken for modules in the first place.
+const specsRoot = path.join(qaRoot, '01-specifications');
-if (fs.existsSync(qaRoot)) {
- const topDirs = fs.readdirSync(qaRoot).filter(d => {
- return fs.statSync(path.join(qaRoot, d)).isDirectory() && !SKIP_DIRS.has(d);
- });
+if (fs.existsSync(specsRoot)) {
+ const moduleDirs = fs.readdirSync(specsRoot).filter(d =>
+ fs.statSync(path.join(specsRoot, d)).isDirectory()
+ );
- for (const moduleDir of topDirs) {
- const modulePath = path.join(qaRoot, moduleDir);
+ for (const moduleDir of moduleDirs) {
+ const modulePath = path.join(specsRoot, moduleDir);
const subDirs = fs.readdirSync(modulePath).filter(d =>
fs.statSync(path.join(modulePath, d)).isDirectory()
);
@@ -90,15 +90,15 @@ if (fs.existsSync(qaRoot)) {
const subPath = path.join(modulePath, subDir);
for (const specFile of SPEC_FILES) {
if (!fs.existsSync(path.join(subPath, specFile))) {
- errors.push(`Missing spec file: qa/${moduleDir}/${subDir}/${specFile}`);
+ errors.push(`Missing spec file: qa/01-specifications/${moduleDir}/${subDir}/${specFile}`);
}
}
// Strict: automation spec must exist
if (strict) {
- const specTs = path.join(qaRoot, '07-automation', 'e2e', moduleDir, `${subDir}.spec.ts`);
+ const specTs = path.join(qaRoot, '07-automation', 'e2e', 'tests', moduleDir, `${subDir}.spec.ts`);
if (!fs.existsSync(specTs)) {
- warnings.push(`[STRICT] No automation spec found: qa/07-automation/e2e/${moduleDir}/${subDir}.spec.ts`);
+ warnings.push(`[STRICT] No automation spec found: qa/07-automation/e2e/tests/${moduleDir}/${subDir}.spec.ts`);
}
}
}
@@ -147,7 +147,7 @@ for (const f of specFiles) {
// -----------------------------------------------------------------------
console.log('');
if (errors.length === 0 && warnings.length === 0) {
- console.log('✅ Validation passed — no issues found.');
+ console.log('✅ Validation passed - no issues found.');
process.exit(0);
}
diff --git a/skills/qa-ado-integration/SKILL.md b/skills/qa-ado-integration/SKILL.md
index 2d612d9..283b3ab 100644
--- a/skills/qa-ado-integration/SKILL.md
+++ b/skills/qa-ado-integration/SKILL.md
@@ -12,31 +12,31 @@ description: >
# QA Skill: ADO Integration (Optional Stage)
-**Stage**: Optional — ADO Integration (no pipeline order dependency)
+**Stage**: Optional - ADO Integration (no pipeline order dependency)
**Prerequisite**: `Plan-de-Pruebas-{proyecto}-Sprint-{N}-{modulo}.md` exists with a complete Tabla de Pruebas (steps filled in); valid `ADO_PAT` env var
**Output**: ADO Test Plan populated with Test Suites and Test Cases (with steps) + `qa/08-azure-integration/module-registry.json` updated
-**Use any time** after Stage 3 (Plan de Pruebas) is ready — Stage 4 and 5 are not required
+**Use any time** after Stage 3 (Plan de Pruebas) is ready - Stage 4 and 5 are not required
> **Credential rule**: NEVER hardcode PAT, username, or organization URL in any file. Always read from env vars or `` in documentation.
-> **Steps rule**: ADO Test Cases must have steps. Do not sync TCs from a Plan de Pruebas with empty or summary-only steps — run Stage 4 first to expand them.
+> **Steps rule**: ADO Test Cases must have steps. Do not sync TCs from a Plan de Pruebas with empty or summary-only steps - run Stage 4 first to expand them.
---
## Inputs Required
-1. `qa/qa-framework.config.json` — ADO org URL, project name, test plan ID (or "create new")
-2. `qa/02-test-plans/sprints/Sprint-{N}/Plan-de-Pruebas-{proyecto}-Sprint-{N}-{modulo}.md` — one file per module; each file's Tabla de Pruebas becomes one Test Suite in ADO
-3. `ADO_PAT` environment variable — personal access token with Test Plans read/write
+1. `qa/qa-framework.config.json` - ADO org URL, project name, test plan ID (or "create new")
+2. `qa/02-test-plans/sprints/Sprint-{N}/Plan-de-Pruebas-{proyecto}-Sprint-{N}-{modulo}.md` - one file per module; each file's Tabla de Pruebas becomes one Test Suite in ADO
+3. `ADO_PAT` environment variable - personal access token with Test Plans read/write
4. Optionally: `qa/08-azure-integration/module-registry.json` for existing ID mapping
> **Note**: The consolidated `Plan-de-Pruebas-{proyecto}-Sprint-{N}.md` (without module suffix)
-> is for human reference only — do not use it as input for ADO scripts.
+> is for human reference only - do not use it as input for ADO scripts.
---
## Process
-### Step 1 — Verify ADO config
+### Step 1 - Verify ADO config
Check `qa/qa-framework.config.json` for:
```json
@@ -48,25 +48,25 @@ Check `qa/qa-framework.config.json` for:
```
If `planId` is null, a new plan will be created in Step 2.
-### Step 2 — Create or verify Test Plan
+### Step 2 - Create or verify Test Plan
Use the PowerShell script: `integrations/ado-powershell/scripts/create-testplan-from-mapping.ps1`
See `references/scripts-and-config.md` for usage, parameters, and required env vars.
Required env vars:
-- `$env:ADO_ORG` — organization URL (e.g., `https://dev.azure.com/myorg`)
-- `$env:ADO_PROJECT` — project name
-- `$env:ADO_PAT` — PAT token
+- `$env:ADO_ORG` - organization URL (e.g., `https://dev.azure.com/myorg`)
+- `$env:ADO_PROJECT` - project name
+- `$env:ADO_PAT` - PAT token
-### Step 3 — Sync test cases from Plan de Pruebas
+### Step 3 - Sync test cases from Plan de Pruebas
For each `Plan-de-Pruebas-{proyecto}-Sprint-{N}-{modulo}.md` file:
1. Read the Tabla de Pruebas
2. Create one **Test Suite** in ADO named after the module (or use the suite groupings from section 5 of the plan)
3. For each row in the table, create one **Test Case** with:
- Title: the Título column value
- - Steps: the Steps column value (numbered steps, ` `-separated → each becomes one ADO step)
+ - Steps: the Steps column value (numbered steps, ` `-separated -> each becomes one ADO step)
- Expected result: the Resultado Esperado column value
- Tags: Tipo + Prioridad values
4. Write the returned ADO Test Case IDs back into the table (column "ADO WI") and into `module-registry.json`
@@ -75,19 +75,19 @@ Use `inject-ado-ids.ps1` to automate the create + inject cycle.
After sync, each row in the Plan de Pruebas table must have an ADO WI ID.
-### Step 4 — Configure Playwright ADO reporter
+### Step 4 - Configure Playwright ADO reporter
See `references/scripts-and-config.md` for the reporter config block to add to `qa/07-automation/e2e/playwright.config.ts`.
Required fields:
- `orgUrl`, `projectName`, `planId`, `runName`
-- All values must come from environment variables — no hardcoded IDs
+- All values must come from environment variables - no hardcoded IDs
-### Step 5 — Run sync and validate
+### Step 5 - Run sync and validate
Run `sync-ado-titles.ps1` after automation runs to update ADO test outcomes from Playwright report JSON.
-### Step 6 — Update registry
+### Step 6 - Update registry
Update `qa/08-azure-integration/module-registry.json` with:
- Module name
@@ -112,6 +112,6 @@ Update `qa/08-azure-integration/module-registry.json` with:
## Outputs
- ADO Test Plan populated with test cases
-- `qa/03-test-cases/automated/TC-*.md` — ADO IDs injected
+- `qa/03-test-cases/automated/TC-*.md` - ADO IDs injected
- `qa/08-azure-integration/module-registry.json` updated
- `qa/07-automation/e2e/playwright.config.ts` updated with reporter config
diff --git a/skills/qa-ado-integration/references/scripts-and-config.md b/skills/qa-ado-integration/references/scripts-and-config.md
index c1257b7..84c295e 100644
--- a/skills/qa-ado-integration/references/scripts-and-config.md
+++ b/skills/qa-ado-integration/references/scripts-and-config.md
@@ -18,7 +18,7 @@ Creates a new ADO Test Plan with suites and test cases from the mapping JSON fil
-MappingFile .\qa\08-azure-integration\ado-ids-mapping-{project}.json
```
-Output: `TestPlanId` and `SuiteIds` — record in `module-registry.json`.
+Output: `TestPlanId` and `SuiteIds` - record in `module-registry.json`.
---
@@ -104,7 +104,7 @@ Apply with `-DryRun:$false`.
"specsPath": "qa/07-automation/e2e/tests/{module-kebab}",
"planId": 0,
"suiteId": 0,
- "description": "{Module display name} — {N} submodules"
+ "description": "{Module display name} - {N} submodules"
}
]
}
@@ -130,7 +130,7 @@ Add to `qa/07-automation/playwright.config.ts` reporter array:
}]
```
-**`isDisabled: !process.env.CI`** — prevents publishing from local developer runs.
+**`isDisabled: !process.env.CI`** - prevents publishing from local developer runs.
---
@@ -194,8 +194,8 @@ steps:
## Credential Security Rules
-1. `ADO_PAT` is **never** committed to any file — always from env var or CI variable group
+1. `ADO_PAT` is **never** committed to any file - always from env var or CI variable group
2. `ADO_PAT` is **never** logged, printed, or written to any markdown file
3. If a PAT appears in any log or output: revoke it immediately in ADO Portal, generate new one
-4. Variable group `qa-secrets` must be scoped to the pipeline only — no project-wide access
-5. PAT minimum scopes: `Work Items (Read, Write)`, `Test Management (Read, Write)` — never use full-access PATs
+4. Variable group `qa-secrets` must be scoped to the pipeline only - no project-wide access
+5. PAT minimum scopes: `Work Items (Read, Write)`, `Test Management (Read, Write)` - never use full-access PATs
diff --git a/skills/qa-automation/SKILL.md b/skills/qa-automation/SKILL.md
index 4e47e4f..e24e7e1 100644
--- a/skills/qa-automation/SKILL.md
+++ b/skills/qa-automation/SKILL.md
@@ -12,10 +12,10 @@ description: >
# QA Skill: Automation Generation (Stage 5 of 6)
-**Stage**: 5 — Automation
+**Stage**: 5 - Automation
**Prerequisite**: All specs for the target submodule are complete and contain no `PENDING-CODE` sections
**Output**: `qa/07-automation/e2e/tests/{module}/{submodule}.spec.ts` + supporting files
-**Next stage**: Stage 6 — Maintenance (`qa-maintenance`) or Stage 5b — Stabilization (`qa-test-stabilization`) if tests fail
+**Next stage**: Stage 6 - Maintenance (`qa-maintenance`) or Stage 5b - Stabilization (`qa-test-stabilization`) if tests fail
> **Pipeline rule**: Never automate a TC whose spec contains `PENDING-CODE`. Resolve spec gaps first.
@@ -23,27 +23,27 @@ description: >
## Inputs Required
-1. `qa/01-specifications/{module}/` — full spec set for target submodule
-2. `qa/07-automation/e2e/playwright.config.ts` — confirm project name maps to submodule tag
-3. `qa/07-automation/e2e/fixtures/auth.ts` — identify available auth fixtures
-4. TC list (from test plan or Stage 4) — defines which TCs to automate in this session
-5. `qa/qa-framework.config.json` → screenshotPath, automationRoot
+1. `qa/01-specifications/{module}/` - full spec set for target submodule
+2. `qa/07-automation/e2e/playwright.config.ts` - confirm project name maps to submodule tag
+3. `qa/07-automation/e2e/fixtures/auth.ts` - identify available auth fixtures
+4. TC list (from test plan or Stage 4) - defines which TCs to automate in this session
+5. `qa/qa-framework.config.json` -> screenshotPath, automationRoot
---
## Process
-### Step 0 — Pre-inspection (MANDATORY before writing any test code)
+### Step 0 - Pre-inspection (MANDATORY before writing any test code)
For every new submodule, run a dedicated inspection script before writing tests.
-**Template**: `references/dom-inspection-template.js` — copy, set the 4 constants at the top, run.
+**Template**: `references/dom-inspection-template.js` - copy, set the 4 constants at the top, run.
-1. Copy `references/dom-inspection-template.js` → `qa/07-automation/e2e/_inspect-{submodule}.js`
+1. Copy `references/dom-inspection-template.js` -> `qa/07-automation/e2e/_inspect-{submodule}.js`
2. Set the 4 constants at the top of the script:
- - `MODULE_ROUTE` — the submodule's URL path (e.g. `'/Users'`, `'/Products/list'`)
- - `APP_SHELL_SEL` — a selector that confirms the SPA has loaded (nav, sidebar, app shell)
- - `CREATE_BTN` — regex matching the create/new button label in this app
- - `BASE_URL` — already read from `process.env.QA_BASE_URL`
+ - `MODULE_ROUTE` - the submodule's URL path (e.g. `'/Users'`, `'/Products/list'`)
+ - `APP_SHELL_SEL` - a selector that confirms the SPA has loaded (nav, sidebar, app shell)
+ - `CREATE_BTN` - regex matching the create/new button label in this app
+ - `BASE_URL` - already read from `process.env.QA_BASE_URL`
3. Run: `QA_BASE_URL= node _inspect-{submodule}.js`
4. Paste key findings as a comment block at the **top of the `.spec.ts` file** before writing any test:
```
@@ -59,14 +59,14 @@ The template covers 5 inspection areas automatically: SPA warmup, list view (gri
**Never skip this step for submodules inside complex forms (tabs, dialogs, nested entities).
The cost of one inspection run is far lower than 10+ debugging iterations.**
-### Step 1 — Scan for blockers
+### Step 1 - Scan for blockers
Before writing a line of code:
-- Search all spec files for `PENDING-CODE` — stop and flag if found
+- Search all spec files for `PENDING-CODE` - stop and flag if found
- Confirm `playwright.config.ts` exists; scaffold it if missing using `references/config-checklist.md`
- Verify `fixtures/auth.ts` has the roles required by this submodule
-### Step 1b — Decide POM vs inline locators
+### Step 1b - Decide POM vs inline locators
Create a Page Object in `qa/07-automation/e2e/page-objects/{SubmoduleName}Page.ts` when ANY of these is true:
- The submodule has a form with 5+ fields (locators will be reused across P0 + P1 suites)
@@ -77,7 +77,7 @@ Otherwise, inline locators are acceptable for simple catalog submodules (single
POM template: `references/pom-template.md`
-### Step 2 — Scaffold spec file
+### Step 2 - Scaffold spec file
Full spec file template and 7 implementation patterns: `references/patterns.md`
@@ -87,30 +87,30 @@ Required scaffold elements:
- `test.describe('{Submodule Name}', () => { ... })`
- Use fixture-based auth (never hardcode credentials)
-### Step 3 — Implement tests in priority order
+### Step 3 - Implement tests in priority order
Implement P0 TCs first, then P1. Within each priority, follow scenario order from `05-test-scenarios.md`.
For each TC:
1. Map TC preconditions to `beforeAll`/`beforeEach` setup
2. Write navigation to the starting URL (use relative paths, not hardcoded base URL)
-3. Use `test.step()` to group logical sub-actions — improves traceability
-4. Assert observable outcomes — avoid asserting internal implementation details
+3. Use `test.step()` to group logical sub-actions - improves traceability
+4. Assert observable outcomes - avoid asserting internal implementation details
-### Step 4 — Apply stability rules
+### Step 4 - Apply stability rules
-- Never use `waitForTimeout` — use `waitForSelector`, `waitForResponse`, or role-based locators
+- Never use `waitForTimeout` - use `waitForSelector`, `waitForResponse`, or role-based locators
- Prefer `getByRole`, `getByLabel`, `getByTestId` over CSS selectors
- Locators attached to dynamic data must use `EXEC_IDX` suffix
-- All test data cleared in `afterAll` — never leave residue
+- All test data cleared in `afterAll` - never leave residue
-### Step 4b — Assertion polarity check (MANDATORY before committing any test)
+### Step 4b - Assertion polarity check (MANDATORY before committing any test)
Every assertion must be verified against the spec, not against the app's
observed output.
**Rule**: if the app does X but the spec requires NOT X, use `test.fail()`
-with the correct assertion — do not adapt the assertion to match the app.
+with the correct assertion - do not adapt the assertion to match the app.
Red flags that require review before proceeding:
- `expect(X).toBe(false)` where the spec describes a positive condition
@@ -118,7 +118,27 @@ Red flags that require review before proceeding:
- Assertions inside an `if (condition)` that only execute on the happy path
- A test that starts passing after a refactor without a clear spec justification
-### Step 5 — Module Completion Checklist
+### Step 5 - Static Check, Smoke Run & Completion Checklist
+
+Two gates run **before** the checklist below. Both must come back clean.
+
+**Gate 1 - static check.** From the automation root (`qa/07-automation/e2e`):
+
+```
+npx tsc --noEmit
+```
+
+Must exit 0. Do not proceed with pending TypeScript errors.
+
+**Gate 2 - smoke run.** Run every new or modified spec at least once against the official runner:
+
+```
+npx playwright test {spec} --project={module} --reporter=list
+```
+
+The goal is to surface reference, import and syntax errors that only appear at execution time - a variable used but never declared passes every review and fails only when the line runs. A full green suite is Stage 5b's job, not this gate's: a test that fails on a real assertion has still cleared Gate 2.
+
+Only when both gates are clean, continue:
Before marking the submodule as ✅ Automation Complete:
- [ ] All P0 TCs passing in CI
@@ -127,6 +147,8 @@ Before marking the submodule as ✅ Automation Complete:
- [ ] `playwright.config.ts` project includes submodule tag
- [ ] `qa/README.md` automation status updated
- [ ] AGENT-NEXT-STEPS.md active sprint updated (remove completed items)
+- [ ] `npx tsc --noEmit` run, exit 0
+- [ ] New/modified specs run at least once via `npx playwright test` (smoke)
---
diff --git a/skills/qa-automation/references/config-checklist.md b/skills/qa-automation/references/config-checklist.md
index e7accf9..675cec9 100644
--- a/skills/qa-automation/references/config-checklist.md
+++ b/skills/qa-automation/references/config-checklist.md
@@ -12,7 +12,7 @@ import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
timeout: 30_000, // Per-test timeout
- actionTimeout: 10_000, // Per-action timeout — REQUIRED, prevents silent hangs
+ actionTimeout: 10_000, // Per-action timeout - REQUIRED, prevents silent hangs
navigationTimeout: 60_000, // Per-navigation timeout (SPAs may need longer)
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : 2,
@@ -31,7 +31,7 @@ export default defineConfig({
reporter: [
['html', { outputFolder: 'playwright-report' }],
- // ADO reporter — add only when integration is enabled:
+ // ADO reporter - add only when integration is enabled:
// ['@alex_neo/playwright-azure-reporter', {
// orgUrl: process.env.ADO_ORG,
// projectName: process.env.ADO_PROJECT,
@@ -59,7 +59,7 @@ export default defineConfig({
Before marking automation as complete, confirm:
-- [ ] `actionTimeout` is set — **not optional**; without it, `textContent()` and similar calls silently hang
+- [ ] `actionTimeout` is set - **not optional**; without it, `textContent()` and similar calls silently hang
- [ ] `testIgnore` excludes seed scripts and debug files
- [ ] `baseURL` reads from `process.env.QA_BASE_URL` (never hardcoded)
- [ ] `storageState` path exists (created by `globalSetup`)
@@ -72,7 +72,7 @@ Before marking automation as complete, confirm:
## global-setup.ts Checklist
-- [ ] Reads every credential from `process.env` — no hardcoded values
+- [ ] Reads every credential from `process.env` - no hardcoded values
- [ ] Uses `evaluate()` for password input (trace safety)
- [ ] Saves storageState to `.auth/session.json` (or per-role paths)
- [ ] Handles login failure explicitly (does not silently save an unauthenticated state)
diff --git a/skills/qa-automation/references/dom-inspection-template.js b/skills/qa-automation/references/dom-inspection-template.js
index 317eef9..3fb7146 100644
--- a/skills/qa-automation/references/dom-inspection-template.js
+++ b/skills/qa-automation/references/dom-inspection-template.js
@@ -1,5 +1,5 @@
/**
- * DOM / API Inspection Script — {SUBMODULE_DISPLAY_NAME}
+ * DOM / API Inspection Script - {SUBMODULE_DISPLAY_NAME}
*
* Purpose: Capture real UI structure before writing automation specs.
* Run ONCE per submodule. Output drives selector and fixture decisions.
@@ -32,7 +32,7 @@
const { chromium } = require('@playwright/test');
const path = require('path');
-// ── Configuration — edit these 4 constants, leave the rest ───────────────────
+// ── Configuration - edit these 4 constants, leave the rest ───────────────────
const STATE_FILE = path.resolve(__dirname, '.auth/user-default.json');
const BASE_URL = process.env.QA_BASE_URL || '';
const MODULE_ROUTE = '/{EntityRoute}'; // e.g. '/Users', '/Orders/list'
@@ -54,7 +54,7 @@ async function run() {
console.log('BASE_URL:', BASE_URL);
console.log('MODULE_ROUTE:', MODULE_ROUTE);
- // ── 0. Intercept API calls — runs passively throughout the entire session ──
+ // ── 0. Intercept API calls - runs passively throughout the entire session ──
const apiCalls = [];
page.on('request', req => {
try {
@@ -148,7 +148,7 @@ async function run() {
).catch(() => []);
console.log('