qa-engineer 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPATIBILITY.md +25 -15
- package/README.md +20 -2
- package/package.json +4 -3
- package/packages/installer/lib/agents/registry.mjs +46 -0
- package/packages/installer/lib/commands/install.mjs +31 -4
- package/packages/installer/lib/core/config.mjs +1 -1
- package/packages/installer/lib/core/lockfile.mjs +1 -1
- package/packages/installer/lib/core/manifest.mjs +3 -0
- package/packages/installer/package.json +1 -1
- package/shared/analysis/lib/qa_analysis/cli.py +15 -0
- package/shared/analysis/lib/qa_analysis/har.py +32 -3
- package/shared/analysis/lib/qa_analysis/junit.py +25 -1
- package/shared/analysis/lib/qa_analysis/redaction.py +10 -2
- package/shared/analysis/lib/qa_analysis/report_html.py +781 -0
- package/skills/qa-api/references/deterministic-tooling.md +2 -0
- package/skills/qa-api/references/evidence-and-reporting.md +29 -4
- package/skills/qa-audit/references/deterministic-tooling.md +2 -0
- package/skills/qa-audit/references/evidence-and-reporting.md +29 -4
- package/skills/qa-debug/references/deterministic-tooling.md +2 -0
- package/skills/qa-debug/references/evidence-and-reporting.md +29 -4
- package/skills/qa-explore/SKILL.md +22 -5
- package/skills/qa-explore/contracts/explore-result.schema.json +26 -2
- package/skills/qa-explore/references/deterministic-tooling.md +130 -0
- package/skills/qa-explore/references/evidence-and-reporting.md +29 -4
- package/skills/qa-explore/references/report-pipeline.md +89 -8
- package/skills/qa-fix/references/deterministic-tooling.md +2 -0
- package/skills/qa-fix/references/evidence-and-reporting.md +29 -4
- package/skills/qa-flaky/references/deterministic-tooling.md +2 -0
- package/skills/qa-flaky/references/evidence-and-reporting.md +29 -4
- package/skills/qa-generate/references/evidence-and-reporting.md +29 -4
- package/skills/qa-init/references/deterministic-tooling.md +2 -0
- package/skills/qa-init/references/evidence-and-reporting.md +29 -4
- package/skills/qa-report/SKILL.md +3 -2
- package/skills/qa-report/references/deterministic-tooling.md +2 -0
- package/skills/qa-report/references/evidence-and-reporting.md +29 -4
- package/skills/qa-review/references/evidence-and-reporting.md +29 -4
- package/skills/qa-run/references/deterministic-tooling.md +2 -0
- package/skills/qa-run/references/evidence-and-reporting.md +29 -4
- package/packages/installer/lib/core/schema-validate.mjs +0 -142
|
@@ -61,6 +61,8 @@ Framework-agnostic parsing, redaction, and validation.
|
|
|
61
61
|
| `validate` | `python3 <skill-dir>/scripts/qa_tool.py analysis validate <instance.json> <schema.json>` | `{valid: bool, errors: [...]}`; exit 1 when invalid |
|
|
62
62
|
| `classify` | `python3 <skill-dir>/scripts/qa_tool.py analysis classify "<error message>" [--http-status N]` | `{classification, confidence, reason}` from the shared taxonomy |
|
|
63
63
|
| `context` | `python3 <skill-dir>/scripts/qa_tool.py analysis context [--root DIR] [--path .qa/context.md]` | The parsed, schema-validated project context as JSON |
|
|
64
|
+
| `report-html` | `python3 <skill-dir>/scripts/qa_tool.py analysis report-html <result.json> [--out report.html]` | The result rendered as one self-contained HTML report — every required field, footer included. Run it instead of writing HTML |
|
|
65
|
+
| `branding` | `python3 <skill-dir>/scripts/qa_tool.py analysis branding --format markdown\|html\|text` | The exact attribution footer bytes for a **rendered** report (never for a JSON artifact) |
|
|
64
66
|
|
|
65
67
|
## 3. Diagnostic engine — `qa_tool.py diagnostics`
|
|
66
68
|
|
|
@@ -27,17 +27,42 @@ Any skill that reaches a conclusion — a detected fact, a chosen strategy, a cl
|
|
|
27
27
|
| Confidence | Calibrated per rule 4; omitted rather than invented when the skill did not weigh alternatives |
|
|
28
28
|
| Recommendations | The next action, named concretely — often the next command and the artifact to feed it |
|
|
29
29
|
|
|
30
|
+
## Rendering the HTML report
|
|
31
|
+
|
|
32
|
+
**Never type the HTML.** When a skill's result conforms to a contract the renderer
|
|
33
|
+
supports, the HTML report is generated from that artifact:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis report-html <result.json> --out <report.html>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The reason is not tidiness. A hand-written report is a second, lossy copy of the
|
|
40
|
+
artifact, and it loses exactly what the reader needs: the first live `/qa-explore`
|
|
41
|
+
run wrote findings whose `actual`, `expected`, and `fixDirection` were all required,
|
|
42
|
+
all present in the JSON, and all absent from the page — collapsed into one sentence
|
|
43
|
+
that left the reader to guess what correct behaviour would have been. The renderer
|
|
44
|
+
cannot make that mistake, because the fields are in its template.
|
|
45
|
+
|
|
46
|
+
What the renderer guarantees per finding: severity, the defect, **what happens
|
|
47
|
+
now**, **what should happen instead**, how to reproduce it, the fix direction, and
|
|
48
|
+
every evidence entry — plus the attribution footer, in one self-contained file with
|
|
49
|
+
no external assets.
|
|
50
|
+
|
|
51
|
+
Run `qa_tool.py analysis report-html --help` for the supported contracts. For an
|
|
52
|
+
artifact it does not support, write the HTML by hand from the contract's fields —
|
|
53
|
+
and render **every** field a reader needs, in that order.
|
|
54
|
+
|
|
30
55
|
## Attribution on rendered reports
|
|
31
56
|
|
|
32
57
|
A report a person opens carries a product attribution footer, the way a Lighthouse
|
|
33
58
|
or Allure report does. It is rendered, never typed: the exact bytes come from the
|
|
34
59
|
bundled analysis toolkit, so every report is identical and a wording change is a
|
|
35
|
-
one-file edit.
|
|
60
|
+
one-file edit. `report-html` already embeds it; for any other rendering, ask for it:
|
|
36
61
|
|
|
37
62
|
```bash
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
63
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format markdown
|
|
64
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format html
|
|
65
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format text
|
|
41
66
|
```
|
|
42
67
|
|
|
43
68
|
Append the output as the last element of the rendered document: `html` inside
|
|
@@ -27,17 +27,42 @@ Any skill that reaches a conclusion — a detected fact, a chosen strategy, a cl
|
|
|
27
27
|
| Confidence | Calibrated per rule 4; omitted rather than invented when the skill did not weigh alternatives |
|
|
28
28
|
| Recommendations | The next action, named concretely — often the next command and the artifact to feed it |
|
|
29
29
|
|
|
30
|
+
## Rendering the HTML report
|
|
31
|
+
|
|
32
|
+
**Never type the HTML.** When a skill's result conforms to a contract the renderer
|
|
33
|
+
supports, the HTML report is generated from that artifact:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis report-html <result.json> --out <report.html>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The reason is not tidiness. A hand-written report is a second, lossy copy of the
|
|
40
|
+
artifact, and it loses exactly what the reader needs: the first live `/qa-explore`
|
|
41
|
+
run wrote findings whose `actual`, `expected`, and `fixDirection` were all required,
|
|
42
|
+
all present in the JSON, and all absent from the page — collapsed into one sentence
|
|
43
|
+
that left the reader to guess what correct behaviour would have been. The renderer
|
|
44
|
+
cannot make that mistake, because the fields are in its template.
|
|
45
|
+
|
|
46
|
+
What the renderer guarantees per finding: severity, the defect, **what happens
|
|
47
|
+
now**, **what should happen instead**, how to reproduce it, the fix direction, and
|
|
48
|
+
every evidence entry — plus the attribution footer, in one self-contained file with
|
|
49
|
+
no external assets.
|
|
50
|
+
|
|
51
|
+
Run `qa_tool.py analysis report-html --help` for the supported contracts. For an
|
|
52
|
+
artifact it does not support, write the HTML by hand from the contract's fields —
|
|
53
|
+
and render **every** field a reader needs, in that order.
|
|
54
|
+
|
|
30
55
|
## Attribution on rendered reports
|
|
31
56
|
|
|
32
57
|
A report a person opens carries a product attribution footer, the way a Lighthouse
|
|
33
58
|
or Allure report does. It is rendered, never typed: the exact bytes come from the
|
|
34
59
|
bundled analysis toolkit, so every report is identical and a wording change is a
|
|
35
|
-
one-file edit.
|
|
60
|
+
one-file edit. `report-html` already embeds it; for any other rendering, ask for it:
|
|
36
61
|
|
|
37
62
|
```bash
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
63
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format markdown
|
|
64
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format html
|
|
65
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format text
|
|
41
66
|
```
|
|
42
67
|
|
|
43
68
|
Append the output as the last element of the rendered document: `html` inside
|
|
@@ -61,6 +61,8 @@ Framework-agnostic parsing, redaction, and validation.
|
|
|
61
61
|
| `validate` | `python3 <skill-dir>/scripts/qa_tool.py analysis validate <instance.json> <schema.json>` | `{valid: bool, errors: [...]}`; exit 1 when invalid |
|
|
62
62
|
| `classify` | `python3 <skill-dir>/scripts/qa_tool.py analysis classify "<error message>" [--http-status N]` | `{classification, confidence, reason}` from the shared taxonomy |
|
|
63
63
|
| `context` | `python3 <skill-dir>/scripts/qa_tool.py analysis context [--root DIR] [--path .qa/context.md]` | The parsed, schema-validated project context as JSON |
|
|
64
|
+
| `report-html` | `python3 <skill-dir>/scripts/qa_tool.py analysis report-html <result.json> [--out report.html]` | The result rendered as one self-contained HTML report — every required field, footer included. Run it instead of writing HTML |
|
|
65
|
+
| `branding` | `python3 <skill-dir>/scripts/qa_tool.py analysis branding --format markdown\|html\|text` | The exact attribution footer bytes for a **rendered** report (never for a JSON artifact) |
|
|
64
66
|
|
|
65
67
|
## 3. Diagnostic engine — `qa_tool.py diagnostics`
|
|
66
68
|
|
|
@@ -27,17 +27,42 @@ Any skill that reaches a conclusion — a detected fact, a chosen strategy, a cl
|
|
|
27
27
|
| Confidence | Calibrated per rule 4; omitted rather than invented when the skill did not weigh alternatives |
|
|
28
28
|
| Recommendations | The next action, named concretely — often the next command and the artifact to feed it |
|
|
29
29
|
|
|
30
|
+
## Rendering the HTML report
|
|
31
|
+
|
|
32
|
+
**Never type the HTML.** When a skill's result conforms to a contract the renderer
|
|
33
|
+
supports, the HTML report is generated from that artifact:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis report-html <result.json> --out <report.html>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The reason is not tidiness. A hand-written report is a second, lossy copy of the
|
|
40
|
+
artifact, and it loses exactly what the reader needs: the first live `/qa-explore`
|
|
41
|
+
run wrote findings whose `actual`, `expected`, and `fixDirection` were all required,
|
|
42
|
+
all present in the JSON, and all absent from the page — collapsed into one sentence
|
|
43
|
+
that left the reader to guess what correct behaviour would have been. The renderer
|
|
44
|
+
cannot make that mistake, because the fields are in its template.
|
|
45
|
+
|
|
46
|
+
What the renderer guarantees per finding: severity, the defect, **what happens
|
|
47
|
+
now**, **what should happen instead**, how to reproduce it, the fix direction, and
|
|
48
|
+
every evidence entry — plus the attribution footer, in one self-contained file with
|
|
49
|
+
no external assets.
|
|
50
|
+
|
|
51
|
+
Run `qa_tool.py analysis report-html --help` for the supported contracts. For an
|
|
52
|
+
artifact it does not support, write the HTML by hand from the contract's fields —
|
|
53
|
+
and render **every** field a reader needs, in that order.
|
|
54
|
+
|
|
30
55
|
## Attribution on rendered reports
|
|
31
56
|
|
|
32
57
|
A report a person opens carries a product attribution footer, the way a Lighthouse
|
|
33
58
|
or Allure report does. It is rendered, never typed: the exact bytes come from the
|
|
34
59
|
bundled analysis toolkit, so every report is identical and a wording change is a
|
|
35
|
-
one-file edit.
|
|
60
|
+
one-file edit. `report-html` already embeds it; for any other rendering, ask for it:
|
|
36
61
|
|
|
37
62
|
```bash
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
63
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format markdown
|
|
64
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format html
|
|
65
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format text
|
|
41
66
|
```
|
|
42
67
|
|
|
43
68
|
Append the output as the last element of the rendered document: `html` inside
|
|
@@ -41,7 +41,7 @@ Do not investigate failures here (that is `/qa-debug`) or plan repairs (`/qa-fix
|
|
|
41
41
|
2. **Aggregate deterministically.** Run the engine's summarize step (see Tooling) to compute the test totals, the by-classification breakdown, the ranked findings and recommendations, and the release-readiness verdict.
|
|
42
42
|
3. **Frame for audiences.** Write the executive summary (shippability in a paragraph and a verdict) and the engineering summary (what broke, why, who owns it, in priority order) from the same aggregated data.
|
|
43
43
|
4. **Detail.** Fill the test, failure, coverage, and risk sections; mark coverage unavailable rather than inventing it when there is no coverage data.
|
|
44
|
-
5. **Render.**
|
|
44
|
+
5. **Render.** Write the JSON result first and validate it; the Markdown and HTML are renderings of it, not parallel drafts. Render the HTML with the report renderer (see Tooling) rather than typing it. Append the rendered attribution footer to the Markdown; the renderer embeds it in the HTML, and the JSON result is an interface and carries no footer.
|
|
45
45
|
6. **Report.** Emit the report result and present the Markdown.
|
|
46
46
|
|
|
47
47
|
## Guardrails
|
|
@@ -61,7 +61,8 @@ Invoke the bundled engine through its launcher, as documented in [references/det
|
|
|
61
61
|
| Report aggregator | `python3 <SKILL_DIR>/scripts/qa_tool.py diagnostics summarize --execution-result <path> --diagnosis <path>` | Totals, by-classification breakdown, top-priority findings, release-readiness verdict | Aggregate the structured results manually per the report-aggregation module and mark the report degraded |
|
|
62
62
|
| One-shot pipeline | `python3 <SKILL_DIR>/scripts/qa_tool.py diagnostics report --execution-result <path>` | Diagnosis, plans, and summary in a single call when no diagnosis exists yet | Run `diagnose` then `summarize` separately |
|
|
63
63
|
| Contract self-check | `python3 <SKILL_DIR>/scripts/qa_tool.py analysis validate <report.json> <schema.json>` | `{valid, errors}` before the report is declared complete | None: an unvalidated report is not complete |
|
|
64
|
-
|
|
|
64
|
+
| HTML report renderer | `python3 <SKILL_DIR>/scripts/qa_tool.py analysis report-html <report-result.json> --out report.html` | The self-contained HTML rendering of the validated result, footer included | Write the HTML by hand from the contract fields and say it was not machine-rendered |
|
|
65
|
+
| Attribution footer | `python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format markdown` | The exact footer bytes to append to the **Markdown** rendering (`report-html` already embeds it) | Omit the footer; never retype it |
|
|
65
66
|
|
|
66
67
|
A missing `qa_tool.py` means the engine is not installed.
|
|
67
68
|
|
|
@@ -61,6 +61,8 @@ Framework-agnostic parsing, redaction, and validation.
|
|
|
61
61
|
| `validate` | `python3 <skill-dir>/scripts/qa_tool.py analysis validate <instance.json> <schema.json>` | `{valid: bool, errors: [...]}`; exit 1 when invalid |
|
|
62
62
|
| `classify` | `python3 <skill-dir>/scripts/qa_tool.py analysis classify "<error message>" [--http-status N]` | `{classification, confidence, reason}` from the shared taxonomy |
|
|
63
63
|
| `context` | `python3 <skill-dir>/scripts/qa_tool.py analysis context [--root DIR] [--path .qa/context.md]` | The parsed, schema-validated project context as JSON |
|
|
64
|
+
| `report-html` | `python3 <skill-dir>/scripts/qa_tool.py analysis report-html <result.json> [--out report.html]` | The result rendered as one self-contained HTML report — every required field, footer included. Run it instead of writing HTML |
|
|
65
|
+
| `branding` | `python3 <skill-dir>/scripts/qa_tool.py analysis branding --format markdown\|html\|text` | The exact attribution footer bytes for a **rendered** report (never for a JSON artifact) |
|
|
64
66
|
|
|
65
67
|
## 3. Diagnostic engine — `qa_tool.py diagnostics`
|
|
66
68
|
|
|
@@ -27,17 +27,42 @@ Any skill that reaches a conclusion — a detected fact, a chosen strategy, a cl
|
|
|
27
27
|
| Confidence | Calibrated per rule 4; omitted rather than invented when the skill did not weigh alternatives |
|
|
28
28
|
| Recommendations | The next action, named concretely — often the next command and the artifact to feed it |
|
|
29
29
|
|
|
30
|
+
## Rendering the HTML report
|
|
31
|
+
|
|
32
|
+
**Never type the HTML.** When a skill's result conforms to a contract the renderer
|
|
33
|
+
supports, the HTML report is generated from that artifact:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis report-html <result.json> --out <report.html>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The reason is not tidiness. A hand-written report is a second, lossy copy of the
|
|
40
|
+
artifact, and it loses exactly what the reader needs: the first live `/qa-explore`
|
|
41
|
+
run wrote findings whose `actual`, `expected`, and `fixDirection` were all required,
|
|
42
|
+
all present in the JSON, and all absent from the page — collapsed into one sentence
|
|
43
|
+
that left the reader to guess what correct behaviour would have been. The renderer
|
|
44
|
+
cannot make that mistake, because the fields are in its template.
|
|
45
|
+
|
|
46
|
+
What the renderer guarantees per finding: severity, the defect, **what happens
|
|
47
|
+
now**, **what should happen instead**, how to reproduce it, the fix direction, and
|
|
48
|
+
every evidence entry — plus the attribution footer, in one self-contained file with
|
|
49
|
+
no external assets.
|
|
50
|
+
|
|
51
|
+
Run `qa_tool.py analysis report-html --help` for the supported contracts. For an
|
|
52
|
+
artifact it does not support, write the HTML by hand from the contract's fields —
|
|
53
|
+
and render **every** field a reader needs, in that order.
|
|
54
|
+
|
|
30
55
|
## Attribution on rendered reports
|
|
31
56
|
|
|
32
57
|
A report a person opens carries a product attribution footer, the way a Lighthouse
|
|
33
58
|
or Allure report does. It is rendered, never typed: the exact bytes come from the
|
|
34
59
|
bundled analysis toolkit, so every report is identical and a wording change is a
|
|
35
|
-
one-file edit.
|
|
60
|
+
one-file edit. `report-html` already embeds it; for any other rendering, ask for it:
|
|
36
61
|
|
|
37
62
|
```bash
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
63
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format markdown
|
|
64
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format html
|
|
65
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format text
|
|
41
66
|
```
|
|
42
67
|
|
|
43
68
|
Append the output as the last element of the rendered document: `html` inside
|
|
@@ -27,17 +27,42 @@ Any skill that reaches a conclusion — a detected fact, a chosen strategy, a cl
|
|
|
27
27
|
| Confidence | Calibrated per rule 4; omitted rather than invented when the skill did not weigh alternatives |
|
|
28
28
|
| Recommendations | The next action, named concretely — often the next command and the artifact to feed it |
|
|
29
29
|
|
|
30
|
+
## Rendering the HTML report
|
|
31
|
+
|
|
32
|
+
**Never type the HTML.** When a skill's result conforms to a contract the renderer
|
|
33
|
+
supports, the HTML report is generated from that artifact:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis report-html <result.json> --out <report.html>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The reason is not tidiness. A hand-written report is a second, lossy copy of the
|
|
40
|
+
artifact, and it loses exactly what the reader needs: the first live `/qa-explore`
|
|
41
|
+
run wrote findings whose `actual`, `expected`, and `fixDirection` were all required,
|
|
42
|
+
all present in the JSON, and all absent from the page — collapsed into one sentence
|
|
43
|
+
that left the reader to guess what correct behaviour would have been. The renderer
|
|
44
|
+
cannot make that mistake, because the fields are in its template.
|
|
45
|
+
|
|
46
|
+
What the renderer guarantees per finding: severity, the defect, **what happens
|
|
47
|
+
now**, **what should happen instead**, how to reproduce it, the fix direction, and
|
|
48
|
+
every evidence entry — plus the attribution footer, in one self-contained file with
|
|
49
|
+
no external assets.
|
|
50
|
+
|
|
51
|
+
Run `qa_tool.py analysis report-html --help` for the supported contracts. For an
|
|
52
|
+
artifact it does not support, write the HTML by hand from the contract's fields —
|
|
53
|
+
and render **every** field a reader needs, in that order.
|
|
54
|
+
|
|
30
55
|
## Attribution on rendered reports
|
|
31
56
|
|
|
32
57
|
A report a person opens carries a product attribution footer, the way a Lighthouse
|
|
33
58
|
or Allure report does. It is rendered, never typed: the exact bytes come from the
|
|
34
59
|
bundled analysis toolkit, so every report is identical and a wording change is a
|
|
35
|
-
one-file edit.
|
|
60
|
+
one-file edit. `report-html` already embeds it; for any other rendering, ask for it:
|
|
36
61
|
|
|
37
62
|
```bash
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
63
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format markdown
|
|
64
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format html
|
|
65
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format text
|
|
41
66
|
```
|
|
42
67
|
|
|
43
68
|
Append the output as the last element of the rendered document: `html` inside
|
|
@@ -61,6 +61,8 @@ Framework-agnostic parsing, redaction, and validation.
|
|
|
61
61
|
| `validate` | `python3 <skill-dir>/scripts/qa_tool.py analysis validate <instance.json> <schema.json>` | `{valid: bool, errors: [...]}`; exit 1 when invalid |
|
|
62
62
|
| `classify` | `python3 <skill-dir>/scripts/qa_tool.py analysis classify "<error message>" [--http-status N]` | `{classification, confidence, reason}` from the shared taxonomy |
|
|
63
63
|
| `context` | `python3 <skill-dir>/scripts/qa_tool.py analysis context [--root DIR] [--path .qa/context.md]` | The parsed, schema-validated project context as JSON |
|
|
64
|
+
| `report-html` | `python3 <skill-dir>/scripts/qa_tool.py analysis report-html <result.json> [--out report.html]` | The result rendered as one self-contained HTML report — every required field, footer included. Run it instead of writing HTML |
|
|
65
|
+
| `branding` | `python3 <skill-dir>/scripts/qa_tool.py analysis branding --format markdown\|html\|text` | The exact attribution footer bytes for a **rendered** report (never for a JSON artifact) |
|
|
64
66
|
|
|
65
67
|
## 3. Diagnostic engine — `qa_tool.py diagnostics`
|
|
66
68
|
|
|
@@ -27,17 +27,42 @@ Any skill that reaches a conclusion — a detected fact, a chosen strategy, a cl
|
|
|
27
27
|
| Confidence | Calibrated per rule 4; omitted rather than invented when the skill did not weigh alternatives |
|
|
28
28
|
| Recommendations | The next action, named concretely — often the next command and the artifact to feed it |
|
|
29
29
|
|
|
30
|
+
## Rendering the HTML report
|
|
31
|
+
|
|
32
|
+
**Never type the HTML.** When a skill's result conforms to a contract the renderer
|
|
33
|
+
supports, the HTML report is generated from that artifact:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis report-html <result.json> --out <report.html>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The reason is not tidiness. A hand-written report is a second, lossy copy of the
|
|
40
|
+
artifact, and it loses exactly what the reader needs: the first live `/qa-explore`
|
|
41
|
+
run wrote findings whose `actual`, `expected`, and `fixDirection` were all required,
|
|
42
|
+
all present in the JSON, and all absent from the page — collapsed into one sentence
|
|
43
|
+
that left the reader to guess what correct behaviour would have been. The renderer
|
|
44
|
+
cannot make that mistake, because the fields are in its template.
|
|
45
|
+
|
|
46
|
+
What the renderer guarantees per finding: severity, the defect, **what happens
|
|
47
|
+
now**, **what should happen instead**, how to reproduce it, the fix direction, and
|
|
48
|
+
every evidence entry — plus the attribution footer, in one self-contained file with
|
|
49
|
+
no external assets.
|
|
50
|
+
|
|
51
|
+
Run `qa_tool.py analysis report-html --help` for the supported contracts. For an
|
|
52
|
+
artifact it does not support, write the HTML by hand from the contract's fields —
|
|
53
|
+
and render **every** field a reader needs, in that order.
|
|
54
|
+
|
|
30
55
|
## Attribution on rendered reports
|
|
31
56
|
|
|
32
57
|
A report a person opens carries a product attribution footer, the way a Lighthouse
|
|
33
58
|
or Allure report does. It is rendered, never typed: the exact bytes come from the
|
|
34
59
|
bundled analysis toolkit, so every report is identical and a wording change is a
|
|
35
|
-
one-file edit.
|
|
60
|
+
one-file edit. `report-html` already embeds it; for any other rendering, ask for it:
|
|
36
61
|
|
|
37
62
|
```bash
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
63
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format markdown
|
|
64
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format html
|
|
65
|
+
python3 <SKILL_DIR>/scripts/qa_tool.py analysis branding --format text
|
|
41
66
|
```
|
|
42
67
|
|
|
43
68
|
Append the output as the last element of the rendered document: `html` inside
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
// A dependency-free validator for the JSON Schema subset this project uses.
|
|
2
|
-
// It is the JavaScript twin of the Python contract validator in
|
|
3
|
-
// shared/analysis/lib/qa_analysis/contracts.py: same supported subset, same
|
|
4
|
-
// semantics, so a document that passes one passes the other. That promise is
|
|
5
|
-
// tested, not asserted — tests/parity/validator-cases.json runs through both.
|
|
6
|
-
//
|
|
7
|
-
// Anything outside the subset is a programming error in the schema, reported
|
|
8
|
-
// rather than silently ignored. `allOf`/`if`/`then`/`else` are in the subset
|
|
9
|
-
// because the pack's safety invariants are cross-field implications.
|
|
10
|
-
|
|
11
|
-
// Keep in sync with SUPPORTED_KEYWORDS in
|
|
12
|
-
// shared/analysis/lib/qa_analysis/contracts.py and the table in
|
|
13
|
-
// docs/skills/output-contracts.md (checked by scripts/check-spec-code-sync.mjs).
|
|
14
|
-
const SUPPORTED = new Set([
|
|
15
|
-
'$schema', '$id', 'title', 'description', 'type', 'properties', 'required',
|
|
16
|
-
'additionalProperties', 'items', 'enum', 'const', 'pattern', 'minimum',
|
|
17
|
-
'maximum', 'minItems', 'maxItems', 'minLength', 'maxLength', 'default',
|
|
18
|
-
'examples', 'format', 'allOf', 'if', 'then', 'else',
|
|
19
|
-
]);
|
|
20
|
-
|
|
21
|
-
// RFC 3339 date-time — the same rule as the Python twin.
|
|
22
|
-
const DATE_TIME = /^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(\.\d+)?([Zz]|[+-]\d{2}:\d{2})$/;
|
|
23
|
-
|
|
24
|
-
function typeOf(value) {
|
|
25
|
-
if (value === null) return 'null';
|
|
26
|
-
if (Array.isArray(value)) return 'array';
|
|
27
|
-
if (Number.isInteger(value)) return 'integer';
|
|
28
|
-
return typeof value; // string | number | boolean | object
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function matchesType(value, type) {
|
|
32
|
-
if (type === 'integer') return Number.isInteger(value) && typeof value !== 'boolean';
|
|
33
|
-
if (type === 'number') return typeof value === 'number';
|
|
34
|
-
if (type === 'object') return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
35
|
-
if (type === 'array') return Array.isArray(value);
|
|
36
|
-
return typeOf(value) === type;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** True when value satisfies schema; used for if/then branch selection. */
|
|
40
|
-
function satisfies(value, schema) {
|
|
41
|
-
const probe = [];
|
|
42
|
-
validateNode(value, schema, '', probe);
|
|
43
|
-
return probe.length === 0;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function validateNode(value, schema, pointer, errors) {
|
|
47
|
-
for (const key of Object.keys(schema)) {
|
|
48
|
-
if (!SUPPORTED.has(key)) {
|
|
49
|
-
errors.push(`${pointer || '/'}: schema uses unsupported keyword "${key}"`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (schema.type) {
|
|
54
|
-
const types = Array.isArray(schema.type) ? schema.type : [schema.type];
|
|
55
|
-
if (!types.some((t) => matchesType(value, t))) {
|
|
56
|
-
errors.push(`${pointer || '/'}: expected ${types.join(' | ')}, got ${typeOf(value)}`);
|
|
57
|
-
return; // further checks assume the type held
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
if (Object.prototype.hasOwnProperty.call(schema, 'const') && JSON.stringify(value) !== JSON.stringify(schema.const)) {
|
|
62
|
-
errors.push(`${pointer || '/'}: must equal ${JSON.stringify(schema.const)}`);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (schema.enum && !schema.enum.some((option) => JSON.stringify(option) === JSON.stringify(value))) {
|
|
66
|
-
errors.push(`${pointer || '/'}: ${JSON.stringify(value)} is not one of ${JSON.stringify(schema.enum)}`);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (typeof value === 'string') {
|
|
70
|
-
if (schema.pattern && !new RegExp(schema.pattern).test(value)) {
|
|
71
|
-
errors.push(`${pointer || '/'}: "${value}" does not match /${schema.pattern}/`);
|
|
72
|
-
}
|
|
73
|
-
if (schema.format === 'date-time' && !DATE_TIME.test(value)) {
|
|
74
|
-
errors.push(`${pointer || '/'}: "${value}" is not a valid date-time`);
|
|
75
|
-
}
|
|
76
|
-
if (schema.minLength != null && value.length < schema.minLength) {
|
|
77
|
-
errors.push(`${pointer || '/'}: shorter than minLength ${schema.minLength}`);
|
|
78
|
-
}
|
|
79
|
-
if (schema.maxLength != null && value.length > schema.maxLength) {
|
|
80
|
-
errors.push(`${pointer || '/'}: longer than maxLength ${schema.maxLength}`);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (typeof value === 'number') {
|
|
85
|
-
if (schema.minimum != null && value < schema.minimum) {
|
|
86
|
-
errors.push(`${pointer || '/'}: ${value} is below minimum ${schema.minimum}`);
|
|
87
|
-
}
|
|
88
|
-
if (schema.maximum != null && value > schema.maximum) {
|
|
89
|
-
errors.push(`${pointer || '/'}: ${value} is above maximum ${schema.maximum}`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (Array.isArray(value)) {
|
|
94
|
-
if (schema.minItems != null && value.length < schema.minItems) {
|
|
95
|
-
errors.push(`${pointer || '/'}: has ${value.length} items, needs at least ${schema.minItems}`);
|
|
96
|
-
}
|
|
97
|
-
if (schema.maxItems != null && value.length > schema.maxItems) {
|
|
98
|
-
errors.push(`${pointer || '/'}: has ${value.length} items, allows at most ${schema.maxItems}`);
|
|
99
|
-
}
|
|
100
|
-
if (schema.items) {
|
|
101
|
-
value.forEach((item, index) => validateNode(item, schema.items, `${pointer}/${index}`, errors));
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (matchesType(value, 'object') && (schema.properties || schema.required || schema.additionalProperties === false)) {
|
|
106
|
-
const properties = schema.properties ?? {};
|
|
107
|
-
for (const name of schema.required ?? []) {
|
|
108
|
-
if (!Object.prototype.hasOwnProperty.call(value, name)) {
|
|
109
|
-
errors.push(`${pointer || '/'}: missing required property "${name}"`);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
for (const [name, child] of Object.entries(value)) {
|
|
113
|
-
if (properties[name]) {
|
|
114
|
-
validateNode(child, properties[name], `${pointer}/${name}`, errors);
|
|
115
|
-
} else if (schema.additionalProperties === false) {
|
|
116
|
-
errors.push(`${pointer || '/'}: unexpected property "${name}"`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Applicators last: the invariant layer. `additionalProperties` deliberately
|
|
122
|
-
// does not see properties introduced by these subschemas (JSON Schema
|
|
123
|
-
// 2020-12 rule, matching the Python twin).
|
|
124
|
-
for (const sub of schema.allOf ?? []) {
|
|
125
|
-
validateNode(value, sub, pointer, errors);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (Object.prototype.hasOwnProperty.call(schema, 'if')) {
|
|
129
|
-
const branch = satisfies(value, schema.if) ? schema.then : schema.else;
|
|
130
|
-
if (branch != null) validateNode(value, branch, pointer, errors);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/** Validate `value` against `schema`; returns an array of error strings (empty = valid). */
|
|
135
|
-
export function validate(value, schema) {
|
|
136
|
-
const errors = [];
|
|
137
|
-
validateNode(value, schema, '', errors);
|
|
138
|
-
return errors;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** The supported keyword subset, exported so tooling can assert parity. */
|
|
142
|
-
export const SUPPORTED_KEYWORDS = Object.freeze([...SUPPORTED].sort());
|