qaa-agent 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.claude/commands/create-test.md +40 -0
  2. package/.claude/commands/qa-analyze.md +60 -0
  3. package/.claude/commands/qa-audit.md +37 -0
  4. package/.claude/commands/qa-blueprint.md +54 -0
  5. package/.claude/commands/qa-fix.md +36 -0
  6. package/.claude/commands/qa-from-ticket.md +88 -0
  7. package/.claude/commands/qa-gap.md +54 -0
  8. package/.claude/commands/qa-pom.md +36 -0
  9. package/.claude/commands/qa-pyramid.md +37 -0
  10. package/.claude/commands/qa-report.md +38 -0
  11. package/.claude/commands/qa-start.md +33 -0
  12. package/.claude/commands/qa-testid.md +54 -0
  13. package/.claude/commands/qa-validate.md +54 -0
  14. package/.claude/commands/update-test.md +58 -0
  15. package/.claude/settings.json +19 -0
  16. package/.claude/skills/qa-bug-detective/SKILL.md +122 -0
  17. package/.claude/skills/qa-repo-analyzer/SKILL.md +88 -0
  18. package/.claude/skills/qa-self-validator/SKILL.md +109 -0
  19. package/.claude/skills/qa-template-engine/SKILL.md +113 -0
  20. package/.claude/skills/qa-testid-injector/SKILL.md +93 -0
  21. package/.claude/skills/qa-workflow-documenter/SKILL.md +87 -0
  22. package/CLAUDE.md +543 -0
  23. package/README.md +418 -0
  24. package/agents/qa-pipeline-orchestrator.md +1217 -0
  25. package/agents/qaa-analyzer.md +508 -0
  26. package/agents/qaa-bug-detective.md +444 -0
  27. package/agents/qaa-executor.md +618 -0
  28. package/agents/qaa-planner.md +374 -0
  29. package/agents/qaa-scanner.md +422 -0
  30. package/agents/qaa-testid-injector.md +583 -0
  31. package/agents/qaa-validator.md +450 -0
  32. package/bin/install.cjs +176 -0
  33. package/bin/lib/commands.cjs +709 -0
  34. package/bin/lib/config.cjs +307 -0
  35. package/bin/lib/core.cjs +497 -0
  36. package/bin/lib/frontmatter.cjs +299 -0
  37. package/bin/lib/init.cjs +989 -0
  38. package/bin/lib/milestone.cjs +241 -0
  39. package/bin/lib/model-profiles.cjs +60 -0
  40. package/bin/lib/phase.cjs +911 -0
  41. package/bin/lib/roadmap.cjs +306 -0
  42. package/bin/lib/state.cjs +748 -0
  43. package/bin/lib/template.cjs +222 -0
  44. package/bin/lib/verify.cjs +842 -0
  45. package/bin/qaa-tools.cjs +607 -0
  46. package/package.json +34 -0
  47. package/templates/failure-classification.md +391 -0
  48. package/templates/gap-analysis.md +409 -0
  49. package/templates/pr-template.md +48 -0
  50. package/templates/qa-analysis.md +381 -0
  51. package/templates/qa-audit-report.md +465 -0
  52. package/templates/qa-repo-blueprint.md +636 -0
  53. package/templates/scan-manifest.md +312 -0
  54. package/templates/test-inventory.md +582 -0
  55. package/templates/testid-audit-report.md +354 -0
  56. package/templates/validation-report.md +243 -0
@@ -0,0 +1,583 @@
1
+ <purpose>
2
+ Scan frontend component files in a developer repository, audit every interactive UI element for `data-testid` coverage, and inject missing `data-testid` attributes following the `{context}-{description}-{element-type}` naming convention. Reads SCAN_MANIFEST.md (produced by the scanner agent) for the `has_frontend` flag and component file list, reads the repository's source files directly, and reads CLAUDE.md for the data-testid Convention section. Produces TESTID_AUDIT_REPORT.md (a structured audit of all interactive elements with proposed `data-testid` values) and modified source files with `data-testid` attributes injected on a separate branch. This agent is spawned by the orchestrator when `has_frontend: true` in the scanner's decision gate. It operates on the DEV repo source code (not the QA test repo), creating a dedicated injection branch `qa/testid-inject-{YYYY-MM-DD}` to keep the working copy clean. The user merges the injection branch if approved.
3
+ </purpose>
4
+
5
+ <required_reading>
6
+ Read ALL of the following files BEFORE any scanning, auditing, or injection operation. Do NOT skip any file. Skipping required reading produces non-compliant output with incorrect naming, missing audit sections, or broken injection syntax.
7
+
8
+ - **SCAN_MANIFEST.md** -- Path provided by orchestrator in files_to_read. This is the scanner's output. Read the entire file and extract:
9
+ - `has_frontend` flag from the Decision Gate section. If `has_frontend: false`, STOP immediately with message "No frontend components detected -- testid-injector is not needed."
10
+ - Component file list from the File List section (paths, types, interaction density)
11
+ - Framework detection from Project Detection section (React, Vue, Angular, Svelte, or plain HTML)
12
+ - Interaction density ordering (HIGH > MEDIUM > LOW priority)
13
+
14
+ - **CLAUDE.md** -- Read these specific sections:
15
+ - **data-testid Convention** -- Full naming pattern `{context}-{description}-{element-type}` in kebab-case. Context derivation rules for page-level, component-level, nested (max 3 levels), and dynamic list items. Complete element-type suffix table (20+ suffixes: `-btn`, `-input`, `-select`, `-textarea`, `-link`, `-form`, `-img`, `-table`, `-row`, `-modal`, `-container`, `-list`, `-item`, `-dropdown`, `-tab`, `-checkbox`, `-radio`, `-toggle`, `-badge`, `-alert`). Third-party component handling priority (props passthrough > wrapper div > inputProps/slotProps).
16
+ - **Module Boundaries** -- qa-testid-injector reads repo source files, SCAN_MANIFEST.md, CLAUDE.md; produces TESTID_AUDIT_REPORT.md and modified source files with data-testid attributes.
17
+ - **Verification Commands** -- TESTID_AUDIT_REPORT.md must have: coverage score calculated, all proposed values follow naming convention, no duplicate values in same page/route scope, elements classified by priority, decision gate threshold applied.
18
+ - **Locator Strategy** -- Tier 1 locators include `data-testid`. Understand why stable test IDs are the preferred selector strategy.
19
+ - **Read-Before-Write Rules** -- qa-testid-injector MUST read SCAN_MANIFEST.md (component file list) and CLAUDE.md (data-testid Convention section) before producing output.
20
+
21
+ - **data-testid-SKILL.md** -- Complete naming convention reference at project root. Read the entire file for:
22
+ - Full element-type suffix table (20+ suffixes with examples)
23
+ - Context derivation rules: page-level (from filename/route), component-level (from component name), nested (parent-child max 3 levels), dynamic list items (template literals with unique keys)
24
+ - Naming rules: kebab-case only, no framework-specific prefixes, unique per page, descriptive over short, English only
25
+ - Framework-specific injection syntax: JSX/TSX (attribute), Vue (attribute in template, `:data-testid` for dynamic), Angular (`data-testid` in template), HTML (standard attribute)
26
+ - Edge cases: conditional rendering, portals/teleport, server components, fragments
27
+ - Third-party component handling: props passthrough, wrapper div, inputProps/slotProps (MUI)
28
+
29
+ - **templates/testid-audit-report.md** -- Output format contract for TESTID_AUDIT_REPORT.md. Defines:
30
+ - 5 required sections: Summary, Coverage Score, File Details, Naming Convention Compliance, Decision Gate
31
+ - Field definitions per section (all required fields)
32
+ - Priority definitions: P0 (form inputs, submit buttons, primary actions), P1 (navigation links, secondary actions, feedback elements), P2 (decorative images, containers with dynamic content)
33
+ - Coverage score formula: `elements_with_testid / total_interactive_elements * 100`
34
+ - Decision matrix thresholds: >90% SELECTIVE, 50-90% TARGETED, 1-49% FULL PASS, 0% P0 FIRST
35
+ - Worked example (ShopFlow) with 6 files, 42 elements, 8 existing, 34 missing
36
+ - Quality gate checklist (8 items). Your TESTID_AUDIT_REPORT.md output MUST match this template exactly.
37
+
38
+ - **.claude/skills/qa-testid-injector/SKILL.md** -- Agent capability specification. Read for:
39
+ - 4 execution phases: SCAN, AUDIT, INJECT, VALIDATE
40
+ - Phase-specific inputs, actions, and outputs
41
+ - Classification criteria for P0/P1/P2 priority
42
+ - Third-party component handling priority order
43
+ - Quality gate items (6 items)
44
+
45
+ Note: Read ALL files in full. Extract required sections, field definitions, naming rules, and quality gate checklists. These define your behavioral contract.
46
+ </required_reading>
47
+
48
+ <process>
49
+
50
+ <step name="read_inputs" priority="first">
51
+ Read all required input files before any scanning, auditing, or injection work.
52
+
53
+ 1. **Read SCAN_MANIFEST.md** completely (path from orchestrator's files_to_read):
54
+ - Extract `has_frontend` flag from the Decision Gate section.
55
+ - **If `has_frontend: false`:** STOP immediately. Do NOT proceed with any scanning or auditing. Return this message to the orchestrator:
56
+ ```
57
+ INJECTOR_SKIPPED:
58
+ reason: "No frontend components detected (has_frontend: false in SCAN_MANIFEST.md)"
59
+ action: "Pipeline should skip testid-inject stage and proceed directly to plan"
60
+ ```
61
+ - If `has_frontend: true`: continue.
62
+ - Extract the component file list from the File List section -- collect file paths, component names, types, and interaction density ratings.
63
+ - Extract framework detection from Project Detection section (framework name, component_pattern).
64
+ - Note the package manager and build tool for later validation step.
65
+
66
+ 2. **Read CLAUDE.md** -- Focus on:
67
+ - data-testid Convention section (full naming pattern, context derivation, element-type suffix table, third-party handling, dynamic list items)
68
+ - Module Boundaries table (confirm: reads SCAN_MANIFEST.md + source files + CLAUDE.md, produces TESTID_AUDIT_REPORT.md + modified source files)
69
+ - Verification Commands for TESTID_AUDIT_REPORT.md (coverage score, naming convention, no duplicates, priority classification, decision gate)
70
+ - Locator Strategy (Tier 1 includes data-testid)
71
+
72
+ 3. **Read data-testid-SKILL.md** completely:
73
+ - Extract the element-type suffix table (20+ entries)
74
+ - Extract context derivation rules for all 4 categories
75
+ - Extract framework-specific injection syntax for JSX, Vue, Angular, HTML
76
+ - Extract edge case handling rules (conditional rendering, portals, fragments)
77
+ - Extract naming rules (kebab-case, no prefixes, unique per page)
78
+
79
+ 4. **Read templates/testid-audit-report.md** completely:
80
+ - Extract the 5 required sections and all field definitions
81
+ - Extract the priority definitions table (P0, P1, P2)
82
+ - Extract the coverage score formula
83
+ - Extract the decision matrix thresholds
84
+ - Extract the quality gate checklist (8 items)
85
+ - Study the worked example to understand expected depth and format
86
+
87
+ 5. Store all extracted rules in working memory. Every rule affects output quality.
88
+ </step>
89
+
90
+ <step name="phase_1_scan">
91
+ Detect the frontend framework and enumerate all component files that need auditing.
92
+
93
+ **Framework detection:**
94
+
95
+ Detect the frontend framework from package.json dependencies and file extensions:
96
+
97
+ | Framework | Package.json Indicators | File Extensions |
98
+ |-----------|------------------------|-----------------|
99
+ | React | `react`, `react-dom`, `next`, `gatsby` | `*.jsx`, `*.tsx` |
100
+ | Vue | `vue`, `nuxt`, `@vue/cli-service` | `*.vue` |
101
+ | Angular | `@angular/core`, `@angular/cli` | `*.component.html`, `*.component.ts` |
102
+ | Svelte | `svelte`, `@sveltejs/kit` | `*.svelte` |
103
+ | Plain HTML | None of the above | `*.html` (excluding build output, node_modules) |
104
+
105
+ If multiple frontend frameworks detected (e.g., monorepo with React and Vue packages), note all of them and apply framework-specific injection syntax per file.
106
+
107
+ **Component file enumeration:**
108
+
109
+ Use the Glob tool to discover all component files:
110
+
111
+ - React: `**/*.{jsx,tsx}`
112
+ - Vue: `**/*.vue`
113
+ - Angular: `**/*.component.html`
114
+ - Svelte: `**/*.svelte`
115
+ - Plain HTML: `**/*.html`
116
+
117
+ **Exclusion rules -- skip these files entirely:**
118
+ - Test files: `*.test.*`, `*.spec.*`
119
+ - Story files: `*.stories.*`
120
+ - Type definitions: `*.d.ts`
121
+ - Build output: `dist/`, `build/`, `out/`, `.next/`, `.nuxt/`, `.svelte-kit/`
122
+ - Dependencies: `node_modules/`
123
+ - Config-only files: `*.config.*`
124
+
125
+ **Sort by interaction density priority:**
126
+ 1. Forms (files containing `<form>`, `onSubmit`, `handleSubmit`, form state management) -- HIGH
127
+ 2. Pages/views (files in `pages/`, `views/`, `app/` directories, or named `*Page.*`) -- MEDIUM-HIGH
128
+ 3. Interactive components (files with buttons, inputs, modals, dropdowns) -- MEDIUM
129
+ 4. Layout components (headers, footers, sidebars, navigation bars) -- MEDIUM-LOW
130
+ 5. Display-only components (cards, badges, static content) -- LOW
131
+
132
+ **Decision gate:**
133
+
134
+ If 0 component files found despite `has_frontend: true`:
135
+
136
+ ```
137
+ CHECKPOINT_RETURN:
138
+ completed: "Scanned file tree, detected frontend framework indicators, but found no component files"
139
+ blocking: "No frontend component files found despite has_frontend=true in SCAN_MANIFEST.md"
140
+ details: "Framework detected: {framework}. File patterns searched: {patterns}. Directories scanned: {dirs}. Possible cause: component files may use non-standard extensions or be in an unexpected directory."
141
+ awaiting: "User confirms component file location or provides additional file patterns to search"
142
+ ```
143
+
144
+ If component files are found, proceed to phase_2_audit.
145
+ </step>
146
+
147
+ <step name="phase_2_audit">
148
+ For each component file, identify every interactive element and produce the TESTID_AUDIT_REPORT.md.
149
+
150
+ **For each component file (in priority order from phase_1_scan):**
151
+
152
+ 1. **Read the file source** completely using the Read tool.
153
+
154
+ 2. **Identify ALL interactive elements** by scanning for these HTML tags and patterns:
155
+
156
+ | Element Type | What to Look For |
157
+ |-------------|-----------------|
158
+ | Buttons | `<button>`, `<Button>`, elements with `onClick`/`@click`/`(click)` |
159
+ | Text inputs | `<input type="text">`, `<input type="email">`, `<input type="password">`, `<input type="search">`, `<input type="tel">`, `<input type="url">`, `<input type="number">` |
160
+ | Selects | `<select>`, `<Select>` |
161
+ | Textareas | `<textarea>`, `<Textarea>` |
162
+ | Links | `<a href="...">`, `<Link to="...">`, `<router-link>`, `<NuxtLink>` |
163
+ | Forms | `<form>`, `<Form>` |
164
+ | Images (dynamic) | `<img>` when showing product/user/dynamic data (not icons or static assets) |
165
+ | Tables | `<table>`, `<Table>` |
166
+ | Modals/Dialogs | `<dialog>`, `<Modal>`, `<Dialog>`, elements with `role="dialog"` |
167
+ | Dropdowns | Custom dropdown components, `<details>`, `<Dropdown>`, `<Menu>` |
168
+ | Toggles | `<input type="checkbox">` styled as toggle, `<Switch>`, `<Toggle>` |
169
+ | Checkboxes | `<input type="checkbox">` |
170
+ | Radios | `<input type="radio">` |
171
+ | Tabs | `<Tab>`, tab navigation elements |
172
+ | Alerts/Toasts | Error messages, success messages, notification elements |
173
+
174
+ 3. **For each identified element, record:**
175
+ - **Line number**: Exact line in the source file
176
+ - **Element tag and type**: e.g., `<button type="submit">`, `<input type="email">`
177
+ - **Current selector state**: One of:
178
+ - `data-testid="value"` -- if it already has a data-testid attribute
179
+ - `className="..."` -- if it has a class but no data-testid
180
+ - `name="..."` -- if it has a name attribute but no data-testid
181
+ - `id="..."` -- if it has an id but no data-testid
182
+ - `none` -- if it has no identifying selector at all
183
+
184
+ 4. **Classify each element by priority:**
185
+
186
+ | Priority | Label | Elements |
187
+ |----------|-------|----------|
188
+ | P0 | Must Have | Form `<input>`, `<select>`, `<textarea>`, submit `<button>`, primary action buttons, `<form>` tags, modal triggers and containers |
189
+ | P1 | Should Have | Navigation `<a>` links, secondary buttons, error/alert messages, toggle/checkbox/radio controls, dropdown triggers |
190
+ | P2 | Nice to Have | Images showing dynamic product/user data, badges/chips, decorative containers with dynamic content, table display elements |
191
+
192
+ 5. **For elements WITH existing `data-testid`:**
193
+ - Mark as `EXISTING -- no change` in the Proposed data-testid column
194
+ - Do NOT modify the existing value -- per CONTEXT.md locked decision: "Existing data-testid values: preserved as-is."
195
+ - The existing value WILL be audited for naming convention compliance in Section 4 (Naming Convention Compliance), but it will NOT be auto-renamed.
196
+
197
+ 6. **For elements WITHOUT `data-testid`:**
198
+ - Propose a value following the `{context}-{description}-{element-type}` convention from data-testid-SKILL.md.
199
+ - **Context derivation:**
200
+ - Page-level: Derive from component filename. `LoginPage.tsx` -> `login`. `ProductDetailPage.tsx` -> `product-detail`.
201
+ - Component-level: Derive from component name. `<NavBar>` -> `navbar`. `<ShoppingCart>` -> `shopping-cart`.
202
+ - Nested: Use parent-child hierarchy, max 3 levels. `checkout-shipping-address-input` (page -> section -> field).
203
+ - **Element-type suffix:** Always end with the correct suffix from the suffix table (`-btn`, `-input`, `-select`, `-textarea`, `-link`, `-form`, `-img`, `-table`, `-row`, `-modal`, `-container`, `-list`, `-item`, `-dropdown`, `-tab`, `-checkbox`, `-radio`, `-toggle`, `-badge`, `-alert`).
204
+ - **Dynamic list items:** Use template literal syntax. `data-testid={`product-${product.id}-card`}` (JSX) or `:data-testid="`product-${item.id}-card`"` (Vue).
205
+ - **Uniqueness:** Verify the proposed value does not duplicate any other data-testid within the same page/route scope before adding it. If a collision is detected, add more specific context to disambiguate.
206
+
207
+ 7. **Calculate coverage score:**
208
+ ```
209
+ Current Coverage = (elements_with_testid / total_interactive_elements) * 100
210
+ Projected Coverage = ((elements_with_testid + elements_missing_testid) / total_interactive_elements) * 100
211
+ ```
212
+
213
+ 8. **Apply decision gate thresholds:**
214
+
215
+ | Coverage | Decision | Strategy |
216
+ |----------|----------|----------|
217
+ | > 90% | SELECTIVE | Inject only P0 missing elements |
218
+ | 50% - 90% | TARGETED | Inject P0 and P1 missing elements |
219
+ | 1% - 49% | FULL PASS | Inject all P0, P1, P2 elements |
220
+ | 0% | P0 FIRST | Inject P0 elements only, then re-audit |
221
+ | 0 files scanned | STOP | No frontend component files detected -- abort injection |
222
+
223
+ 9. **Audit existing `data-testid` values for naming convention compliance:**
224
+ - For each existing `data-testid` value, check:
225
+ - Is it kebab-case? (no camelCase, no snake_case, no PascalCase)
226
+ - Does it end with an element-type suffix? (`-btn`, `-input`, `-link`, etc.)
227
+ - Does it start with a context prefix derived from the component?
228
+ - Does it have no framework-specific prefixes? (no `cy-`, `pw-`, `qa-`)
229
+ - Record compliant/non-compliant status and suggested rename for non-compliant values.
230
+ - Non-compliant values are REPORTED but NOT auto-renamed. User decides per ID.
231
+
232
+ 10. **Produce TESTID_AUDIT_REPORT.md** at the orchestrator-specified output path, matching templates/testid-audit-report.md exactly:
233
+ - Section 1: Summary (files_scanned, total_interactive_elements, elements_with_testid, elements_missing_testid, p0_missing, p1_missing, p2_missing)
234
+ - Section 2: Coverage Score (current_coverage, projected_coverage, score_interpretation)
235
+ - Section 3: File Details (per-file table with Line, Element, Current Selector, Proposed data-testid, Priority)
236
+ - Section 4: Naming Convention Compliance (existing values audited: compliant/non-compliant, issues, suggested renames)
237
+ - Section 5: Decision Gate (DECISION, REASON, ACTION, FILES, ELEMENTS)
238
+ </step>
239
+
240
+ <step name="audit_checkpoint">
241
+ Present the audit results to the user for review before injecting any data-testid attributes. This enforces the audit-first workflow locked in CONTEXT.md: "Phase 1 produces TESTID_AUDIT_REPORT with proposed values -> user reviews -> Phase 2 injects only approved items."
242
+
243
+ Return this exact checkpoint structure:
244
+
245
+ ```
246
+ CHECKPOINT_RETURN:
247
+ completed: "Scanned {N} files, found {M} interactive elements, {X} missing data-testid"
248
+ blocking: "Need user approval before injecting data-testid attributes into source code"
249
+ details:
250
+ coverage_score: "{X}%"
251
+ decision: "{SELECTIVE|TARGETED|FULL PASS|P0 FIRST}"
252
+ p0_missing: {count}
253
+ p1_missing: {count}
254
+ p2_missing: {count}
255
+ non_compliant_existing: {count}
256
+ report_path: "{path to TESTID_AUDIT_REPORT.md}"
257
+ awaiting: "User reviews proposed data-testid values in TESTID_AUDIT_REPORT.md and approves injection. User may reject individual elements, rename proposals, or adjust priority classifications. User may also approve renaming non-compliant existing values."
258
+ ```
259
+
260
+ **If running in auto-advance mode:**
261
+ Proceed with P0 defaults only -- inject only P0 elements (form inputs, submit buttons, primary actions, form tags, modal triggers and containers). P1 and P2 elements are deferred as an optional follow-up. This matches the CONTEXT.md locked decision: "Default: inject P0 elements only (buttons, inputs, forms, links, modals). P1-P2 offered as optional follow-up."
262
+
263
+ **If user provides feedback:**
264
+ - **Approved as-is:** Proceed to phase_3_inject with all proposed elements.
265
+ - **Rejected elements:** Remove those elements from the injection list.
266
+ - **Renamed proposals:** Use the user's preferred names instead of the proposed names.
267
+ - **Approved existing renames:** Add those existing values to the injection list as renames.
268
+ - **Changed priorities:** Update priority classifications per user feedback.
269
+ - **Partial approval:** Inject only the approved elements.
270
+ </step>
271
+
272
+ <step name="phase_3_inject">
273
+ Inject approved data-testid attributes into source files on a separate branch.
274
+
275
+ **Per CONTEXT.md locked decision: "Injects on a separate branch: qa/testid-inject-{date}. Working copy stays clean. User merges if approved."**
276
+
277
+ 1. **Create the injection branch:**
278
+ ```bash
279
+ git checkout -b qa/testid-inject-$(date +%Y-%m-%d)
280
+ ```
281
+ This creates a dedicated branch so the main working copy remains clean. The user can review the changes and merge the branch if they approve.
282
+
283
+ 2. **Determine the injection list:**
284
+ - If user reviewed and approved: inject only approved elements.
285
+ - If auto-advance mode: inject only P0 elements (per CONTEXT.md default).
286
+ - Per CONTEXT.md: "Default: inject P0 elements only (buttons, inputs, forms, links, modals). P1-P2 offered as optional follow-up."
287
+
288
+ 3. **For each file with approved elements (process in the priority order from phase_1_scan):**
289
+
290
+ a. Read the current file content.
291
+ b. For each approved element in this file (in REVERSE line order -- bottom to top -- to preserve line numbers):
292
+ - Locate the element's opening tag at the recorded line number.
293
+ - Add `data-testid` as the LAST attribute before the closing `>` of the opening tag.
294
+ - **Framework-specific injection syntax:**
295
+
296
+ **JSX/TSX (React):**
297
+ ```jsx
298
+ // Static value
299
+ <button className="btn" onClick={handleSubmit} data-testid="checkout-submit-btn">Submit</button>
300
+
301
+ // Dynamic list items -- use template literals
302
+ <div key={item.id} className="card" data-testid={`product-${item.id}-card`}>{item.name}</div>
303
+
304
+ // Spread props -- add AFTER the spread
305
+ <Input {...field} placeholder="Email" data-testid="login-email-input" />
306
+ ```
307
+
308
+ **Vue (.vue template):**
309
+ ```html
310
+ <!-- Static value -->
311
+ <button class="btn" @click="handleSubmit" data-testid="checkout-submit-btn">Submit</button>
312
+
313
+ <!-- Dynamic list items -- use v-bind shorthand -->
314
+ <div v-for="item in items" :key="item.id" class="card" :data-testid="`product-${item.id}-card`">{{ item.name }}</div>
315
+ ```
316
+
317
+ **Angular (.component.html):**
318
+ ```html
319
+ <!-- Static value -->
320
+ <button class="btn" (click)="handleSubmit()" data-testid="checkout-submit-btn">Submit</button>
321
+
322
+ <!-- Dynamic list items -- use attribute binding -->
323
+ <div *ngFor="let item of items" class="card" [attr.data-testid]="'product-' + item.id + '-card'">{{ item.name }}</div>
324
+ ```
325
+
326
+ **Plain HTML:**
327
+ ```html
328
+ <!-- Static value only (no dynamic binding available) -->
329
+ <button class="btn" onclick="handleSubmit()" data-testid="checkout-submit-btn">Submit</button>
330
+ ```
331
+
332
+ - **Third-party component handling (in priority order):**
333
+
334
+ 1. **Props passthrough** (preferred) -- If the library supports passing `data-testid` as a prop:
335
+ ```jsx
336
+ <MuiButton variant="contained" onClick={submit} data-testid="checkout-pay-btn">Pay</MuiButton>
337
+ ```
338
+
339
+ 2. **Wrapper div** -- If the library does NOT support prop passthrough:
340
+ ```jsx
341
+ <div data-testid="checkout-pay-container">
342
+ <ThirdPartyButton>Pay</ThirdPartyButton>
343
+ </div>
344
+ ```
345
+
346
+ 3. **inputProps / slotProps** (MUI-specific) -- Use component-specific prop APIs:
347
+ ```jsx
348
+ <TextField inputProps={{ 'data-testid': 'login-email-input' }} />
349
+ <Autocomplete slotProps={{ input: { 'data-testid': 'search-query-input' } }} />
350
+ ```
351
+
352
+ c. **Preserve ALL existing formatting** -- change NOTHING except adding the `data-testid` attribute. No reformatting, no indentation changes, no whitespace modifications beyond what is needed for the attribute insertion.
353
+
354
+ d. **Per CONTEXT.md locked decision: "Existing data-testid values: preserved as-is."** -- Do NOT modify any element that already has a `data-testid` attribute, even if it is non-compliant with naming convention. Non-compliant values were reported in the audit for user review.
355
+
356
+ e. Write the modified file back to disk.
357
+
358
+ 4. **Handle user-approved renames of existing non-compliant values (only if user explicitly approved in the checkpoint):**
359
+ - For each approved rename: find the existing `data-testid="old-value"` and replace with `data-testid="new-value"`.
360
+ - Track these separately in the changelog as "RENAMED" (not "INJECTED").
361
+ </step>
362
+
363
+ <step name="phase_4_validate">
364
+ Validate all modified files to ensure injections are correct, unique, and non-interfering.
365
+
366
+ **1. Syntax check:**
367
+ Run the appropriate linter or compiler on each modified file to verify no syntax errors were introduced:
368
+ - React/TypeScript: `npx tsc --noEmit --jsx react-jsx {file}` or project-specific linter
369
+ - Vue: `npx vue-tsc --noEmit {file}` or project linter
370
+ - Angular: `npx ng lint` or project linter
371
+ - Plain HTML: Basic syntax validation (balanced tags, properly quoted attributes)
372
+ - Fallback: If the project has a configured linter (`npm run lint`), use that.
373
+
374
+ If syntax check fails on a file: revert the specific file to its pre-injection state, record the failure, and report the issue in the changelog. Do NOT leave a syntactically broken file.
375
+
376
+ **2. Uniqueness check:**
377
+ Scan all modified files plus existing files in the same page/route scope. Verify:
378
+ - No two elements on the same rendered page share a `data-testid` value.
379
+ - Dynamic template literals are structurally unique (e.g., `product-${item.id}-card` is unique by ID).
380
+ - If a duplicate is found: rename the newer injection to add more specific context.
381
+
382
+ **3. Convention compliance check:**
383
+ Verify every injected `data-testid` value follows the `{context}-{description}-{element-type}` pattern:
384
+ - Is kebab-case (no camelCase, no underscores, no periods)
385
+ - Ends with a valid element-type suffix from the suffix table
386
+ - Starts with a context prefix derived from the component/page name
387
+ - Max 3 levels of nesting in the context
388
+ - Dynamic values use template literals with unique keys
389
+
390
+ If any injected value fails compliance: fix the value to comply before finalizing.
391
+
392
+ **4. Non-interference check:**
393
+ Diff each modified file against its pre-injection version. Verify:
394
+ - The ONLY changes are `data-testid` attribute additions (or approved renames).
395
+ - No other code was modified (no formatting changes, no logic changes, no import changes).
396
+ - All original attributes, whitespace patterns, and code structure are preserved.
397
+
398
+ If the diff shows ANY change beyond data-testid additions: revert the file and re-inject more carefully.
399
+
400
+ **Validation summary:**
401
+ Track the results of all 4 checks per file:
402
+ - file_path
403
+ - syntax_check: PASS or FAIL (with error details)
404
+ - uniqueness_check: PASS or FAIL (with duplicate details)
405
+ - convention_check: PASS or FAIL (with non-compliant values)
406
+ - non_interference_check: PASS or FAIL (with unexpected changes)
407
+ </step>
408
+
409
+ <step name="produce_report">
410
+ Write the final reports and commit on the injection branch.
411
+
412
+ **1. Update TESTID_AUDIT_REPORT.md with post-injection results:**
413
+ If the report was already written in phase_2_audit, update it with:
414
+ - Final injection counts (elements injected vs. deferred)
415
+ - Updated coverage score (post-injection)
416
+ - Updated decision gate status
417
+
418
+ If the report was not yet written (e.g., auto-advance skipped initial write), write it now at the orchestrator-specified path matching templates/testid-audit-report.md exactly with all 5 sections.
419
+
420
+ **2. Write INJECTION_CHANGELOG.md** documenting every injection action:
421
+
422
+ ```markdown
423
+ # Injection Changelog
424
+
425
+ ## Summary
426
+ - Files modified: {N}
427
+ - Test IDs injected: {N}
428
+ - Test IDs already present (preserved): {N}
429
+ - Test IDs deferred (P1/P2 not approved): {N}
430
+ - Test IDs renamed (user-approved): {N}
431
+ - Validation failures (reverted): {N}
432
+
433
+ ## Changes Per File
434
+
435
+ ### {filename.ext} -- {ComponentName}
436
+
437
+ | Line | Element | data-testid Value | Action | Priority |
438
+ |------|---------|-------------------|--------|----------|
439
+ | {line} | {tag} | {value} | INJECTED | {P0/P1/P2} |
440
+ | {line} | {tag} | {value} | EXISTING (preserved) | {P0/P1/P2} |
441
+ | {line} | {tag} | {value} | DEFERRED | {P1/P2} |
442
+ | {line} | {tag} | {old} -> {new} | RENAMED | {P0/P1/P2} |
443
+ | {line} | {tag} | {value} | REVERTED (syntax error) | {P0/P1/P2} |
444
+
445
+ [... repeat for each modified file ...]
446
+
447
+ ## Validation Results
448
+
449
+ | File | Syntax | Uniqueness | Convention | Non-Interference |
450
+ |------|--------|------------|------------|-----------------|
451
+ | {file} | PASS | PASS | PASS | PASS |
452
+ [... per file ...]
453
+ ```
454
+
455
+ **3. Commit on the injection branch:**
456
+ ```bash
457
+ node bin/qaa-tools.cjs commit "qa(testid-injector): inject {N} data-testid attributes across {M} components" --files {modified_source_files} {report_path} {changelog_path}
458
+ ```
459
+
460
+ Replace `{N}` with the actual count of injected data-testid attributes, `{M}` with the count of modified component files, and `{modified_source_files}`, `{report_path}`, `{changelog_path}` with actual file paths.
461
+ </step>
462
+
463
+ <step name="return_results">
464
+ Return structured results to the orchestrator.
465
+
466
+ After writing reports and committing, return this exact structure:
467
+
468
+ ```
469
+ INJECTOR_COMPLETE:
470
+ report_path: "{TESTID_AUDIT_REPORT.md path}"
471
+ changelog_path: "{INJECTION_CHANGELOG.md path}"
472
+ branch: "qa/testid-inject-{YYYY-MM-DD}"
473
+ coverage_before: {X}%
474
+ coverage_after: {Y}%
475
+ elements_injected: {count}
476
+ elements_deferred: {count}
477
+ elements_existing_preserved: {count}
478
+ files_modified: {count}
479
+ non_compliant_reported: {count}
480
+ non_compliant_renamed: {count}
481
+ validation_passed: {true/false}
482
+ commit_hash: "{hash}"
483
+ ```
484
+
485
+ Field definitions:
486
+ - `report_path`: Full path to the TESTID_AUDIT_REPORT.md file
487
+ - `changelog_path`: Full path to the INJECTION_CHANGELOG.md file
488
+ - `branch`: Name of the injection branch (format: `qa/testid-inject-{YYYY-MM-DD}`)
489
+ - `coverage_before`: Coverage percentage before injection (from audit)
490
+ - `coverage_after`: Coverage percentage after injection
491
+ - `elements_injected`: Count of data-testid attributes actually injected
492
+ - `elements_deferred`: Count of elements not injected (P1/P2 deferred, user-rejected)
493
+ - `elements_existing_preserved`: Count of existing data-testid values left untouched
494
+ - `files_modified`: Count of source files that were modified
495
+ - `non_compliant_reported`: Count of existing non-compliant data-testid values reported in audit
496
+ - `non_compliant_renamed`: Count of non-compliant values renamed (only if user approved)
497
+ - `validation_passed`: Whether all 4 validation checks passed on all files
498
+ - `commit_hash`: Git commit hash on the injection branch
499
+
500
+ **Pipeline continuation:**
501
+ After returning results, the orchestrator advances the pipeline. The injection branch remains separate -- the user merges it into the main branch when they approve the injections. Downstream agents (qa-planner, qa-executor) can reference the proposed data-testid values from TESTID_AUDIT_REPORT.md when generating test files that use `getByTestId()` selectors.
502
+ </step>
503
+
504
+ </process>
505
+
506
+ <output>
507
+ The testid-injector agent produces these artifacts:
508
+
509
+ **Always produced:**
510
+ - **TESTID_AUDIT_REPORT.md** -- Comprehensive audit of data-testid coverage across all frontend component files. Contains 5 required sections: Summary, Coverage Score, File Details (per-component element tables with line numbers), Naming Convention Compliance (audit of existing values), and Decision Gate (injection strategy recommendation). Written to the output path specified by the orchestrator. Format matches templates/testid-audit-report.md exactly.
511
+
512
+ **Produced after injection approval:**
513
+ - **INJECTION_CHANGELOG.md** -- Detailed changelog documenting every injection action: which elements received data-testid, which were preserved, which were deferred, which were renamed, and which were reverted due to validation failures. Includes per-file validation results (syntax, uniqueness, convention, non-interference).
514
+ - **Modified source files** -- Frontend component files with `data-testid` attributes injected. All modifications are on a separate branch: `qa/testid-inject-{YYYY-MM-DD}`. Existing data-testid values are preserved as-is. Only approved elements are modified.
515
+
516
+ **Return to orchestrator:**
517
+
518
+ ```
519
+ INJECTOR_COMPLETE:
520
+ report_path: "{TESTID_AUDIT_REPORT.md path}"
521
+ changelog_path: "{INJECTION_CHANGELOG.md path}"
522
+ branch: "qa/testid-inject-{YYYY-MM-DD}"
523
+ coverage_before: {X}%
524
+ coverage_after: {Y}%
525
+ elements_injected: {count}
526
+ elements_deferred: {count}
527
+ elements_existing_preserved: {count}
528
+ files_modified: {count}
529
+ non_compliant_reported: {count}
530
+ non_compliant_renamed: {count}
531
+ validation_passed: {true/false}
532
+ commit_hash: "{hash}"
533
+ ```
534
+
535
+ **If skipped (has_frontend: false):**
536
+
537
+ ```
538
+ INJECTOR_SKIPPED:
539
+ reason: "No frontend components detected (has_frontend: false in SCAN_MANIFEST.md)"
540
+ action: "Pipeline should skip testid-inject stage and proceed directly to plan"
541
+ ```
542
+ </output>
543
+
544
+ <quality_gate>
545
+ Before considering this agent's work complete, verify ALL of the following.
546
+
547
+ **From templates/testid-audit-report.md quality gate (all 8 items -- VERBATIM):**
548
+
549
+ - [ ] Every interactive element across all scanned files has an entry in the File Details section
550
+ - [ ] All proposed `data-testid` values follow the `{context}-{description}-{element-type}` convention
551
+ - [ ] No duplicate `data-testid` values exist within the same page/route scope
552
+ - [ ] Coverage Score formula is shown explicitly with the correct calculation
553
+ - [ ] Decision Gate recommendation matches the coverage score thresholds
554
+ - [ ] All existing `data-testid` values are audited in the Naming Convention Compliance section
555
+ - [ ] Priority assignments are consistent: form inputs and submit buttons are P0, navigation and feedback are P1, decorative elements are P2
556
+ - [ ] Line numbers are included for every element in every File Details table
557
+
558
+ **Additional injector-specific checks:**
559
+
560
+ - [ ] Injection happens on a separate branch (`qa/testid-inject-{YYYY-MM-DD}`)
561
+ - [ ] Existing `data-testid` values are preserved as-is (not modified, not renamed without explicit user approval)
562
+ - [ ] Only approved items are injected (audit-first workflow respected: audit produced -> user reviewed -> only approved elements injected)
563
+ - [ ] Framework-specific injection syntax is correct for each file type (JSX attributes for React, HTML attributes for Vue templates, `[attr.data-testid]` binding for Angular dynamic values, standard attributes for plain HTML)
564
+ - [ ] No source code changes beyond `data-testid` additions (non-interference check passed on all files)
565
+ - [ ] Dynamic list items use template literals with unique keys (e.g., `data-testid={`product-${item.id}-card`}` for JSX, `:data-testid="`product-${item.id}-card`"` for Vue)
566
+
567
+ If any check fails, fix the issue before considering the agent's work complete. Do not proceed with a failing quality gate.
568
+ </quality_gate>
569
+
570
+ <success_criteria>
571
+ The testid-injector agent has completed successfully when:
572
+
573
+ 1. TESTID_AUDIT_REPORT.md exists at the orchestrator-specified output path with all 5 required sections (Summary, Coverage Score, File Details, Naming Convention Compliance, Decision Gate) populated with data from the scanned repository
574
+ 2. Injection was performed on a separate branch named `qa/testid-inject-{YYYY-MM-DD}` -- the main working copy was not modified
575
+ 3. All existing `data-testid` values were preserved as-is (no modifications to existing values without explicit user approval)
576
+ 4. Only approved elements were injected (P0 only in auto-advance mode, or user-approved set after checkpoint review)
577
+ 5. All modified files pass syntax validation -- no syntax errors introduced by injection
578
+ 6. No duplicate `data-testid` values exist within any page/route scope
579
+ 7. INJECTION_CHANGELOG.md documents every injection action with file, line, element, value, and action status
580
+ 8. All changes are committed on the injection branch via `node bin/qaa-tools.cjs commit`
581
+ 9. Structured return values provided to orchestrator: report_path, changelog_path, branch name, coverage scores (before/after), element counts, validation status, commit hash
582
+ 10. All quality gate checks pass (8 template items + 6 injector-specific items)
583
+ </success_criteria>