qa-engineer 0.9.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/COMPATIBILITY.md +26 -12
  2. package/README.md +182 -84
  3. package/package.json +11 -20
  4. package/packages/engine/bin/qa-engine.mjs +560 -0
  5. package/packages/engine/lib/analysis/branding.mjs +187 -0
  6. package/packages/engine/lib/analysis/context.mjs +294 -0
  7. package/packages/engine/lib/analysis/contracts.mjs +149 -0
  8. package/packages/engine/lib/analysis/diff-guard.mjs +425 -0
  9. package/packages/engine/lib/analysis/discovery.mjs +220 -0
  10. package/packages/engine/lib/analysis/evidence.mjs +116 -0
  11. package/packages/engine/lib/analysis/har.mjs +124 -0
  12. package/packages/engine/lib/analysis/junit.mjs +126 -0
  13. package/packages/engine/lib/analysis/network.mjs +237 -0
  14. package/packages/engine/lib/analysis/redaction.mjs +127 -0
  15. package/packages/engine/lib/analysis/report-html.mjs +76 -0
  16. package/packages/engine/lib/analysis/taxonomy.mjs +109 -0
  17. package/packages/engine/lib/analysis/xml.mjs +153 -0
  18. package/packages/engine/lib/analysis/zip.mjs +107 -0
  19. package/packages/engine/lib/artifacts/manager.mjs +453 -0
  20. package/packages/engine/lib/artifacts/mime.mjs +109 -0
  21. package/packages/engine/lib/artifacts/zip-write.mjs +143 -0
  22. package/packages/engine/lib/diagnostics/engine.mjs +165 -0
  23. package/packages/engine/lib/diagnostics/internal-contracts.mjs +50 -0
  24. package/packages/engine/lib/diagnostics/prioritization.mjs +99 -0
  25. package/packages/engine/lib/diagnostics/repair.mjs +73 -0
  26. package/packages/engine/lib/diagnostics/root-cause.mjs +92 -0
  27. package/packages/engine/lib/diagnostics/timeline.mjs +101 -0
  28. package/packages/engine/lib/frameworks/junit-frameworks.mjs +48 -0
  29. package/packages/engine/lib/frameworks/playwright.mjs +158 -0
  30. package/packages/engine/lib/report/components/charts.mjs +424 -0
  31. package/packages/engine/lib/report/components/evidence.mjs +207 -0
  32. package/packages/engine/lib/report/components/findings.mjs +258 -0
  33. package/packages/engine/lib/report/components/nav.mjs +99 -0
  34. package/packages/engine/lib/report/components/primitives.mjs +246 -0
  35. package/packages/engine/lib/report/components/runtime.mjs +246 -0
  36. package/packages/engine/lib/report/components/timeline.mjs +65 -0
  37. package/packages/engine/lib/report/core/model.mjs +270 -0
  38. package/packages/engine/lib/report/core/normalize.mjs +226 -0
  39. package/packages/engine/lib/report/core/sections.mjs +978 -0
  40. package/packages/engine/lib/report/export/bundle.mjs +293 -0
  41. package/packages/engine/lib/report/export/html.mjs +183 -0
  42. package/packages/engine/lib/report/export/machine.mjs +290 -0
  43. package/packages/engine/lib/report/export/markdown.mjs +323 -0
  44. package/packages/engine/lib/report/schemas/qa-report.schema.json +555 -0
  45. package/packages/engine/lib/report/theme/css.mjs +529 -0
  46. package/packages/engine/lib/report/theme/tokens.mjs +137 -0
  47. package/packages/engine/lib/report/version.mjs +78 -0
  48. package/packages/engine/package.json +14 -0
  49. package/packages/installer/lib/agents/targets.mjs +90 -0
  50. package/packages/installer/lib/agents/user-level.mjs +80 -0
  51. package/packages/installer/lib/cli/flags.mjs +20 -2
  52. package/packages/installer/lib/commands/doctor.mjs +13 -20
  53. package/packages/installer/lib/commands/install.mjs +160 -91
  54. package/packages/installer/lib/commands/repair.mjs +10 -6
  55. package/packages/installer/lib/commands/self-test.mjs +4 -3
  56. package/packages/installer/lib/commands/uninstall.mjs +14 -8
  57. package/packages/installer/lib/commands/update.mjs +9 -4
  58. package/packages/installer/lib/commands/verify.mjs +13 -12
  59. package/packages/installer/lib/constants.mjs +13 -0
  60. package/packages/installer/lib/core/bundle.mjs +69 -92
  61. package/packages/installer/lib/core/conflict.mjs +5 -4
  62. package/packages/installer/lib/core/fs-safe.mjs +146 -6
  63. package/packages/installer/lib/core/integrity.mjs +59 -0
  64. package/packages/installer/lib/core/lockfile.mjs +19 -3
  65. package/packages/installer/lib/core/manifest.mjs +48 -57
  66. package/packages/installer/lib/core/plan.mjs +213 -0
  67. package/packages/installer/lib/core/qa-home.mjs +145 -0
  68. package/packages/installer/lib/core/scope.mjs +274 -0
  69. package/packages/installer/lib/core/validate-install.mjs +49 -31
  70. package/packages/installer/package.json +1 -1
  71. package/packages/installer/schemas/qa-lock.schema.json +119 -21
  72. package/shared/tooling/qa-tool.mjs +161 -0
  73. package/skills/qa-api/SKILL.md +5 -5
  74. package/skills/qa-api/references/deterministic-tooling.md +34 -32
  75. package/skills/qa-api/references/evidence-and-reporting.md +5 -5
  76. package/skills/qa-api/scripts/qa-tool.mjs +162 -0
  77. package/skills/qa-audit/SKILL.md +5 -5
  78. package/skills/qa-audit/references/deterministic-tooling.md +34 -32
  79. package/skills/qa-audit/references/evidence-and-reporting.md +5 -5
  80. package/skills/qa-audit/scripts/qa-tool.mjs +162 -0
  81. package/skills/qa-debug/SKILL.md +6 -6
  82. package/skills/qa-debug/references/deterministic-tooling.md +34 -32
  83. package/skills/qa-debug/references/diagnostic-engine.md +3 -3
  84. package/skills/qa-debug/references/evidence-and-reporting.md +5 -5
  85. package/skills/qa-debug/scripts/qa-tool.mjs +162 -0
  86. package/skills/qa-explore/SKILL.md +31 -15
  87. package/skills/qa-explore/contracts/explore-result.schema.json +517 -11
  88. package/skills/qa-explore/references/api-replay.md +40 -1
  89. package/skills/qa-explore/references/deterministic-tooling.md +34 -32
  90. package/skills/qa-explore/references/evidence-and-reporting.md +5 -5
  91. package/skills/qa-explore/references/report-pipeline.md +266 -96
  92. package/skills/qa-explore/scripts/qa-tool.mjs +162 -0
  93. package/skills/qa-fix/SKILL.md +4 -4
  94. package/skills/qa-fix/references/deterministic-tooling.md +34 -32
  95. package/skills/qa-fix/references/diagnostic-engine.md +3 -3
  96. package/skills/qa-fix/references/evidence-and-reporting.md +5 -5
  97. package/skills/qa-fix/scripts/qa-tool.mjs +162 -0
  98. package/skills/qa-flaky/SKILL.md +4 -4
  99. package/skills/qa-flaky/references/deterministic-tooling.md +34 -32
  100. package/skills/qa-flaky/references/evidence-and-reporting.md +5 -5
  101. package/skills/qa-flaky/scripts/qa-tool.mjs +162 -0
  102. package/skills/qa-generate/references/evidence-and-reporting.md +5 -5
  103. package/skills/qa-init/SKILL.md +4 -4
  104. package/skills/qa-init/references/deterministic-tooling.md +34 -32
  105. package/skills/qa-init/references/evidence-and-reporting.md +5 -5
  106. package/skills/qa-init/scripts/qa-tool.mjs +162 -0
  107. package/skills/qa-report/SKILL.md +7 -7
  108. package/skills/qa-report/references/deterministic-tooling.md +34 -32
  109. package/skills/qa-report/references/diagnostic-engine.md +3 -3
  110. package/skills/qa-report/references/evidence-and-reporting.md +5 -5
  111. package/skills/qa-report/scripts/qa-tool.mjs +162 -0
  112. package/skills/qa-review/references/evidence-and-reporting.md +5 -5
  113. package/skills/qa-run/SKILL.md +6 -6
  114. package/skills/qa-run/references/deterministic-tooling.md +34 -32
  115. package/skills/qa-run/references/evidence-and-reporting.md +5 -5
  116. package/skills/qa-run/scripts/qa-tool.mjs +162 -0
  117. package/shared/analysis/lib/qa_analysis/__init__.py +0 -11
  118. package/shared/analysis/lib/qa_analysis/branding.py +0 -175
  119. package/shared/analysis/lib/qa_analysis/cli.py +0 -144
  120. package/shared/analysis/lib/qa_analysis/context.py +0 -233
  121. package/shared/analysis/lib/qa_analysis/contracts.py +0 -158
  122. package/shared/analysis/lib/qa_analysis/diff_guard.py +0 -327
  123. package/shared/analysis/lib/qa_analysis/discovery.py +0 -113
  124. package/shared/analysis/lib/qa_analysis/evidence.py +0 -128
  125. package/shared/analysis/lib/qa_analysis/har.py +0 -87
  126. package/shared/analysis/lib/qa_analysis/junit.py +0 -104
  127. package/shared/analysis/lib/qa_analysis/redaction.py +0 -107
  128. package/shared/analysis/lib/qa_analysis/report_html.py +0 -781
  129. package/shared/analysis/lib/qa_analysis/taxonomy.py +0 -98
  130. package/shared/diagnostics/lib/qa_diagnostics/__init__.py +0 -13
  131. package/shared/diagnostics/lib/qa_diagnostics/cli.py +0 -124
  132. package/shared/diagnostics/lib/qa_diagnostics/engine.py +0 -143
  133. package/shared/diagnostics/lib/qa_diagnostics/internal_contracts.py +0 -75
  134. package/shared/diagnostics/lib/qa_diagnostics/prioritization.py +0 -101
  135. package/shared/diagnostics/lib/qa_diagnostics/repair.py +0 -72
  136. package/shared/diagnostics/lib/qa_diagnostics/root_cause.py +0 -89
  137. package/shared/diagnostics/lib/qa_diagnostics/timeline.py +0 -71
  138. package/shared/frameworks/cypress/lib/cypress_analysis.py +0 -27
  139. package/shared/frameworks/playwright/lib/playwright_analysis.py +0 -167
  140. package/shared/frameworks/selenium/lib/selenium_analysis.py +0 -28
  141. package/shared/frameworks/webdriverio/lib/webdriverio_analysis.py +0 -24
  142. package/shared/tooling/qa_tool.py +0 -127
  143. /package/{shared/analysis/lib/qa_analysis → packages/engine/lib/analysis}/branding.json +0 -0
  144. /package/{shared → packages/engine/lib}/analysis/schemas/context.schema.json +0 -0
  145. /package/{shared → packages/engine/lib}/diagnostics/schemas/internal/analysis-result.schema.json +0 -0
  146. /package/{shared → packages/engine/lib}/diagnostics/schemas/internal/diagnosis.schema.json +0 -0
  147. /package/{shared → packages/engine/lib}/diagnostics/schemas/internal/execution-result-min.schema.json +0 -0
@@ -0,0 +1,226 @@
1
+ // Every input shape the renderer accepts, folded into one internal shape.
2
+ //
3
+ // ## Why this seam exists
4
+ //
5
+ // The promise is that a report looks the same whoever produced it — Claude Code,
6
+ // Cursor, Codex, a future agent nobody has written yet. That promise is only keepable
7
+ // if *no producer ever touches presentation*, which in turn is only enforceable if
8
+ // every producer hands over the same kind of thing: structured data, validated, and
9
+ // then rendered by code none of them can influence.
10
+ //
11
+ // This module is where "the same kind of thing" is defined. It accepts:
12
+ //
13
+ // qa-engineer/qa-report the canonical, producer-neutral report (schema 2.0)
14
+ // qa-explore/explore-result the exploratory contract (schema 1.x)
15
+ // qa-report/report-result the release rollup (schema 1.x)
16
+ //
17
+ // and returns one internal shape. Adding a fourth producer means adding a normalizer
18
+ // here — not a stylesheet, not a template, and certainly not a second renderer.
19
+ //
20
+ // ## Why the older contracts are not simply migrated
21
+ //
22
+ // Reports are archived. A result written a year ago must still render, and an
23
+ // installed skill emitting schema 1.1 must not break when the pack updates. So the
24
+ // older shapes are translated on read rather than deprecated on write, and the
25
+ // canonical shape is what new producers are pointed at.
26
+ //
27
+ // ## What a producer may not do
28
+ //
29
+ // Nothing here reads a colour, a class name, a font, or a layout hint, and the
30
+ // canonical schema has no field to carry one. An agent that wants its report to look
31
+ // different has no mechanism to make it so — which is the point.
32
+
33
+ import { schemaVersionOf, SUPPORTED_SCHEMA_VERSIONS, isSupportedSchema } from '../version.mjs';
34
+
35
+ export class SchemaError extends Error {
36
+ name = 'SchemaError';
37
+ }
38
+
39
+ /**
40
+ * The canonical report, unwrapped into the internal shape.
41
+ *
42
+ * The canonical form groups fields by audience — `summary` for the decision,
43
+ * `coverage` for what was looked at, `issues` for the detail — because that is how a
44
+ * producer thinks about filling it in. The internal shape is flat because that is how
45
+ * fifteen sections read from it. Neither is wrong; this function is the join.
46
+ */
47
+ function fromCanonical(report) {
48
+ const metadata = report.metadata ?? {};
49
+ const summary = report.summary ?? {};
50
+ const coverage = report.coverage ?? {};
51
+ const recommendations = report.recommendations ?? {};
52
+
53
+ return {
54
+ contract: { name: 'qa-engineer/qa-report', version: report.schemaVersion ?? '2.0' },
55
+ skill: metadata.producer
56
+ ? { name: metadata.producer.skill ?? metadata.producer.agent ?? 'unknown', version: metadata.producer.version ?? '' }
57
+ : undefined,
58
+ producer: metadata.producer ?? null,
59
+
60
+ title: metadata.title,
61
+ url: metadata.url,
62
+ environment: metadata.environment,
63
+ generatedAt: metadata.generatedAt,
64
+ durationMs: metadata.durationMs,
65
+ reportVersion: metadata.reportVersion,
66
+ browserAdapter: metadata.browserAdapter,
67
+ authentication: metadata.authentication,
68
+
69
+ summary: summary.text ?? '',
70
+ classification: summary.classification ?? null,
71
+ executive: {
72
+ verdict: summary.verdict,
73
+ headline: summary.headline,
74
+ health: summary.health,
75
+ risks: summary.risks,
76
+ recommendedAction: summary.recommendedAction,
77
+ estimatedFixHours: summary.estimatedFixHours,
78
+ confidence: summary.confidence,
79
+ },
80
+ scores: summary.scores,
81
+ severityCounts: summary.severityCounts ?? countSeverities(report.issues ?? []),
82
+
83
+ scope: {
84
+ objective: coverage.objective,
85
+ covered: coverage.covered,
86
+ notCovered: coverage.notCovered,
87
+ },
88
+ dimensionsRun: coverage.dimensionsRun,
89
+ pages: coverage.pages,
90
+ testCases: coverage.testCases,
91
+ timeline: coverage.timeline,
92
+
93
+ // `issues` is the canonical name; `findings` is what fifteen sections already call
94
+ // them. Renaming the sections to match would be churn with no reader benefit.
95
+ findings: report.issues ?? [],
96
+ artifacts: report.artifacts,
97
+ evidence: report.evidence ?? [],
98
+
99
+ performance: report.performance,
100
+ security: report.security,
101
+ accessibility: report.accessibility,
102
+ console: report.console,
103
+ network: report.network,
104
+ dbValidation: report.dataValidation,
105
+
106
+ fixOrder: recommendations.fixOrder,
107
+ recommendations: recommendations.actions,
108
+ whatWorksWell: recommendations.whatWorksWell,
109
+ };
110
+ }
111
+
112
+ /** Severity totals, when the producer did not count them itself. */
113
+ function countSeverities(issues) {
114
+ const counts = { critical: 0, high: 0, medium: 0, low: 0 };
115
+ for (const issue of issues) {
116
+ if (counts[issue.severity] !== undefined) counts[issue.severity] += 1;
117
+ }
118
+ return counts;
119
+ }
120
+
121
+ /**
122
+ * Fold `qa-report/report-result` — the release rollup — into the same shape.
123
+ *
124
+ * The rollup describes the same event from further away: one summary of many runs
125
+ * rather than one run of a product. Its readiness verdict becomes the executive
126
+ * verdict and its test summary becomes the test counts. Its failure list stays a list
127
+ * of failures rather than being forced into `findings` with an invented severity for
128
+ * each row — a failing assertion is not a severity-ranked defect, and pretending
129
+ * otherwise would put fabricated numbers in the summary tiles.
130
+ */
131
+ function fromRollup(result) {
132
+ const readiness = result.releaseReadiness ?? {};
133
+ const tests = result.testSummary ?? {};
134
+ const summaries = result.summaries ?? {};
135
+
136
+ return {
137
+ ...result,
138
+ executive: result.executive ?? {
139
+ verdict: readiness.verdict ?? result.classification,
140
+ headline: readiness.rationale ?? result.summary,
141
+ health: summaries.executive ?? null,
142
+ },
143
+ severityCounts: result.severityCounts ?? { critical: 0, high: 0, medium: 0, low: 0 },
144
+ testCases: result.testCases ?? (Object.keys(tests).length > 0
145
+ ? {
146
+ total: tests.total ?? 0,
147
+ passed: tests.passed ?? 0,
148
+ failed: tests.failed ?? 0,
149
+ blocked: tests.blocked ?? 0,
150
+ skipped: tests.skipped ?? 0,
151
+ cases: [],
152
+ }
153
+ : null),
154
+ };
155
+ }
156
+
157
+ /** The exploratory contract is already the internal shape; nothing to translate. */
158
+ function fromExplore(result) {
159
+ return result;
160
+ }
161
+
162
+ const NORMALIZERS = Object.freeze({
163
+ 'qa-engineer/qa-report': fromCanonical,
164
+ 'qa-explore/explore-result': fromExplore,
165
+ 'qa-report/report-result': fromRollup,
166
+ });
167
+
168
+ /** Contract names this renderer accepts, for an error message and for `--help`. */
169
+ export function supportedContracts() {
170
+ return Object.keys(NORMALIZERS).sort();
171
+ }
172
+
173
+ /**
174
+ * Which producer made this report, for the provenance block.
175
+ *
176
+ * Recorded and displayed, never acted on: the renderer's behaviour must not vary by
177
+ * producer, or the guarantee that two agents produce identical output is only true
178
+ * until someone adds a special case.
179
+ */
180
+ export function producerOf(result) {
181
+ const producer = result?.metadata?.producer ?? result?.producer;
182
+ if (producer) {
183
+ return {
184
+ agent: producer.agent ?? null,
185
+ model: producer.model ?? null,
186
+ skill: producer.skill ?? null,
187
+ version: producer.version ?? null,
188
+ };
189
+ }
190
+ if (result?.skill) {
191
+ return { agent: null, model: null, skill: result.skill.name, version: result.skill.version };
192
+ }
193
+ return null;
194
+ }
195
+
196
+ /**
197
+ * Normalize any accepted contract into the internal shape.
198
+ *
199
+ * Throws rather than guessing. A result whose contract is unknown, or whose schema
200
+ * version this renderer has never been tested against, is refused by name — rendering
201
+ * it half-understood would produce a document that looks authoritative and is missing
202
+ * whatever the unknown version added.
203
+ */
204
+ export function normalize(result) {
205
+ const name = result?.contract?.name;
206
+ const normalizer = NORMALIZERS[name];
207
+
208
+ if (!normalizer) {
209
+ throw new SchemaError(
210
+ `no renderer for contract ${name === undefined ? 'None' : `'${name}'`}; supported: ` +
211
+ `${supportedContracts().join(', ')}`,
212
+ );
213
+ }
214
+
215
+ if (!isSupportedSchema(result)) {
216
+ throw new SchemaError(
217
+ `report declares schema version ${schemaVersionOf(result)}, which this renderer ` +
218
+ `does not support; supported: ${SUPPORTED_SCHEMA_VERSIONS.join(', ')}`,
219
+ );
220
+ }
221
+
222
+ const normalized = normalizer(result);
223
+ // Provenance survives normalization so the appendix can state who produced the
224
+ // report even when the contract carried it somewhere else.
225
+ return { ...normalized, producer: producerOf(result) };
226
+ }