@mmerterden/multi-agent-pipeline 20.27.2 → 20.28.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 (65) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +6 -5
  3. package/README.tr.md +6 -5
  4. package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +0 -1
  5. package/docs/architecture.md +4 -4
  6. package/docs/ecosystem.md +5 -5
  7. package/docs/facts.json +14 -12
  8. package/docs/features.md +2 -0
  9. package/install/_dev-only-files.mjs +1 -0
  10. package/install/catalog-history.json +1 -1
  11. package/install/index.mjs +0 -2
  12. package/manifest.json +67 -44
  13. package/package.json +4 -1
  14. package/pipeline/agents/code-reviewer.md +1 -0
  15. package/pipeline/agents/test-gen-analyst.md +98 -0
  16. package/pipeline/commands/multi-agent/sync/SKILL.md +3 -3
  17. package/pipeline/commands/multi-agent/test-gen/SKILL.md +124 -0
  18. package/pipeline/multi-agent-refs/cross-cli-contract.md +11 -5
  19. package/pipeline/multi-agent-refs/features/doctor.md +13 -0
  20. package/pipeline/multi-agent-refs/features/test-classification.md +76 -0
  21. package/pipeline/multi-agent-refs/features/usage-reporting.md +17 -0
  22. package/pipeline/multi-agent-refs/help/en.md +1 -0
  23. package/pipeline/multi-agent-refs/help/tr.md +1 -0
  24. package/pipeline/multi-agent-refs/phases/operations.md +2 -2
  25. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +6 -7
  26. package/pipeline/multi-agent-refs/phases/phase-3-review.md +3 -3
  27. package/pipeline/multi-agent-refs/phases/phase-5-report.md +6 -5
  28. package/pipeline/multi-agent-refs/test-gen/stages.md +227 -0
  29. package/pipeline/preferences-template.json +3 -0
  30. package/pipeline/schemas/prefs.schema.json +14 -0
  31. package/pipeline/schemas/test-gen-discovery.schema.json +73 -0
  32. package/pipeline/schemas/test-gen-gaps.schema.json +122 -0
  33. package/pipeline/schemas/test-gen-inventory.schema.json +59 -0
  34. package/pipeline/schemas/test-gen-results.schema.json +44 -0
  35. package/pipeline/scripts/_handle-patterns.mjs +166 -0
  36. package/pipeline/scripts/_http-post.mjs +141 -0
  37. package/pipeline/scripts/_stack-routing.mjs +4 -0
  38. package/pipeline/scripts/_test-gaming.mjs +227 -0
  39. package/pipeline/scripts/_unified-diff.mjs +104 -0
  40. package/pipeline/scripts/_worktree-snapshot.mjs +113 -0
  41. package/pipeline/scripts/doctor.mjs +51 -0
  42. package/pipeline/scripts/evidence-gate.mjs +74 -3
  43. package/pipeline/scripts/handle-diff-audit.mjs +188 -0
  44. package/pipeline/scripts/routine-registry.mjs +5 -1
  45. package/pipeline/scripts/run-ui-tests.sh +27 -3
  46. package/pipeline/scripts/test-classify.mjs +296 -0
  47. package/pipeline/scripts/test-gen-gate.mjs +409 -0
  48. package/pipeline/scripts/test-gen-state.mjs +313 -0
  49. package/pipeline/scripts/test-integrity-gate.mjs +76 -11
  50. package/pipeline/scripts/uninstall.mjs +1 -0
  51. package/pipeline/scripts/usage-register-beacon.mjs +239 -0
  52. package/pipeline/scripts/usage-register.mjs +128 -33
  53. package/pipeline/scripts/usage-report.mjs +43 -33
  54. package/pipeline/skills/.skill-manifest.json +23 -3
  55. package/pipeline/skills/.skills-index.json +57 -2
  56. package/pipeline/skills/shared/README.md +10 -5
  57. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +3 -3
  58. package/pipeline/skills/shared/core/multi-agent-test-gen/SKILL.md +120 -0
  59. package/pipeline/skills/shared/external/fix-review-verdicts/SKILL.md +133 -0
  60. package/pipeline/skills/shared/external/handle-request/SKILL.md +97 -0
  61. package/pipeline/skills/shared/external/scenario-gap-analysis/SKILL.md +129 -0
  62. package/pipeline/skills/shared/external/scenario-gap-analysis/references/scenario-schema.md +60 -0
  63. package/pipeline/skills/shared/external/test-result-triage/SKILL.md +160 -0
  64. package/pipeline/skills/skills-index.md +7 -2
  65. package/install/_telemetry.mjs +0 -61
@@ -0,0 +1,227 @@
1
+ # test-gen stages
2
+
3
+ <!-- toc -->
4
+ - [Artefacts and gates](#artefacts-and-gates)
5
+ - [Read-only stages](#read-only-stages)
6
+ - [1. Inventory](#1-inventory)
7
+ - [Priority](#priority)
8
+ - [2. Discovery](#2-discovery)
9
+ - [3. Gaps](#3-gaps)
10
+ - [4. Handles](#4-handles)
11
+ - [5. Write](#5-write)
12
+ - [6. Run](#6-run)
13
+ - [7. Classify](#7-classify)
14
+ - [8. Triage](#8-triage)
15
+ - [End: the change base](#end-the-change-base)
16
+ <!-- /toc -->
17
+
18
+ The contract behind `/multi-agent:test-gen`. Each stage is a subagent with one
19
+ artefact under `$RUN` (`$HOME/.claude/logs/test-gen/<repo>/<timestamp>/`), and
20
+ no stage starts until the previous artefact has passed its gate. A stage that
21
+ says it is done without its artefact on disk is not done.
22
+
23
+ ## Artefacts and gates
24
+
25
+ | # | Stage | Runs as | Artefact | Gate |
26
+ |---|---|---|---|---|
27
+ | 1 | inventory | orchestrator, with the toolkit inventory tools | `inventory.json` | `test-gen-gate.mjs check --stage inventory`, `evidence-gate.mjs --claim artefact` |
28
+ | - | priority | AskUserQuestion; autopilot takes the top three | `inventory.json` `priority` | `check --stage inventory` again |
29
+ | 2 | discovery | `test-gen-analyst` (read-only) | `discovery.json` | `check --stage discovery`, `evidence-gate.mjs --claim artefact` |
30
+ | 3 | gaps | `coverage_gap`, then `test-gen-analyst` (read-only) | `gaps.json` | `check --stage gaps`, `evidence-gate.mjs --claim artefact` |
31
+ | 4 | handles | general-purpose subagent, product code, handles only | `handles.md` and the stage's delta | `test-gen-gate.mjs handles`, `handle-diff-audit.mjs --baseline` exit 0 |
32
+ | 5 | write | general-purpose subagent, test files only | the test files and `written.json` | `test-gen-gate.mjs written --baseline` |
33
+ | 6 | run | orchestrator | `run-<k>.log` and per-test reports | `evidence-gate.mjs --claim test` |
34
+ | 7 | classify | orchestrator | `results.json` | `check --stage results` |
35
+ | 8 | triage | orchestrator, result by result | `results.json` `triage` | `test-gen-state.mjs revise` |
36
+
37
+ Before each stage take `STAGE_START=$(date -u +%Y-%m-%dT%H:%M:%SZ)`. The
38
+ artefact gate (`--claim artefact --stage-start "$STAGE_START" --evidence <its own
39
+ file>`) rejects a file written before the stage began and a file that is not
40
+ whole (JSON that does not parse). It is applied to the JSON a model writes
41
+ (inventory, discovery, gaps); the scripts write `results.json` and the
42
+ baselines whole.
43
+
44
+ The two stages that may edit the repository (handles, write) start with a
45
+ snapshot: `test-gen-gate.mjs baseline` records the working tree, untracked files
46
+ included, as a git tree without touching it or the index. Their gates read only
47
+ what changed after it, so uncommitted work the person had before the stage is
48
+ never taken for the stage's change, and never reverted to satisfy a gate.
49
+
50
+ ## Read-only stages
51
+
52
+ Discovery and gaps are dispatched with `subagent_type: "test-gen-analyst"`
53
+ (`agents/test-gen-analyst.md`). The persona's grant is an allowlist: Read, Grep,
54
+ Glob and the read-only toolkit tools (`coverage_gap`, the `repo_*` readers, the
55
+ `code_*` language-server queries). No Write, Edit, Bash, Agent or other MCP tool
56
+ is granted, so an analysis stage cannot change the repository however it is
57
+ prompted. It returns JSON; the orchestrator writes it with the Write tool.
58
+ Copilot CLI installs the same persona with the same grant. Codex CLI has no
59
+ per-agent tool grant: there, run the two stages in the main session and change
60
+ nothing during them.
61
+
62
+ ## 1. Inventory
63
+
64
+ Prefer a tool over reading: `design_scenario_inventory` (a screen's states from
65
+ the design), `web_map` (a web app's routes), the UI tree (`ios_get_ui_tree`,
66
+ `android_get_ui_tree`) of each reachable screen, or the OpenAPI spec. Without a
67
+ tool, read the navigation and routes and set `source: "agent"`. Every item has a
68
+ stable `id`: route, screen id, design node id, endpoint path.
69
+
70
+ When a change base exists (`test-gen-state.mjs base-scan` measured), keep the
71
+ items whose `path` is among the scan's `files` or `graph.affectedFiles`, and say
72
+ how many fell away. A scan with `measured: false` narrows nothing: the inventory
73
+ stays whole and the reason is reported.
74
+
75
+ ## Priority
76
+
77
+ Show the items in one table (id, kind, title, existing tests from a quick grep)
78
+ and ask which to cover (AskUserQuestion, multi-select, following
79
+ `multi-agent-refs/picker-contract.md`). Autopilot asks nothing:
80
+ it takes the three items most likely to hide a failure, in this order: no test
81
+ reaches them, they changed since the base, they are flows rather than single
82
+ screens. Record `priority: {mode, selected}`.
83
+
84
+ ## 2. Discovery
85
+
86
+ `test-gen-analyst`, stage `discovery`, with the selected items. It returns test
87
+ roots and runners, the tests that reach each item (joined by identifier), the
88
+ handles already present, and the conventions for new tests. Save as
89
+ `discovery.json` and gate it with `check --stage discovery`.
90
+
91
+ ## 3. Gaps
92
+
93
+ With `coverage_gap` (toolkit >= v3.25.0): call it with the inventory and the
94
+ test root from discovery; its `covered`, `uncovered` and `unmatched` buckets go
95
+ into the analyst's prompt, and `coverage.method` is `coverage_gap`. Without it:
96
+ `coverage.method` is `agent` and every match the analyst makes is
97
+ `method: "inferred"`. Then `test-gen-analyst`, stage `gaps`. Save its answer as
98
+ `gaps.json`. The scenario-gap-analysis skill (ai-common-toolkit) defines the
99
+ buckets and the scenario fields when it is enabled.
100
+
101
+ Before writing any test, show the gaps and the `dataNeeds[]`. Each need is
102
+ named, never filled: the person supplies the value, or names the credential the
103
+ test resolves by `secretRef`. Autopilot writes only the scenarios whose needs
104
+ are already met and lists the rest as blocked by data.
105
+
106
+ ## 4. Handles
107
+
108
+ Only gaps with `handlesNeeded`. Before dispatch:
109
+
110
+ ```bash
111
+ node "$HOME/.claude/scripts/test-gen-gate.mjs" baseline --repo "$REPO" --out "$RUN/handles-baseline.json"
112
+ ```
113
+
114
+ The subagent adds the missing handles and nothing else: iOS
115
+ `accessibilityIdentifier`, Compose `testTag` (or `testTagsAsResourceId` in a
116
+ semantics block), web `data-testid`. Never a list index, never a renamed or
117
+ moved element, never a handle the accessibility tree cannot see. It writes
118
+ `handles.md`, one line per request: `ADDED file:line`, `ALREADY file:line`,
119
+ `COULD NOT ADD <reason>` (the handle-request skill, when enabled). When nothing
120
+ was missing, the first line is `HANDLES: none-needed` followed by the
121
+ `ALREADY file:line` lines that prove it. Then:
122
+
123
+ ```bash
124
+ node "$HOME/.claude/scripts/test-gen-gate.mjs" handles --repo "$REPO" \
125
+ --baseline "$RUN/handles-baseline.json" --handles "$RUN/handles.md"
126
+ node "$HOME/.claude/scripts/handle-diff-audit.mjs" --repo "$REPO" \
127
+ --baseline "$RUN/handles-baseline.json" > "$RUN/handle-audit.json"
128
+ ```
129
+
130
+ `handles` passes when the stage added a handle line, or on
131
+ `HANDLES: none-needed` with at least one cited line, inside the repository, that
132
+ carries a handle. `handle-diff-audit.mjs` must exit 0: 2 means the stage changed
133
+ more than handles (`violations[]` name the file and the lost, extra or
134
+ reordered tokens), so revert those hunks of the stage's own change and rerun; 3
135
+ means it could not read the diff, which is not a pass.
136
+
137
+ ## 5. Write
138
+
139
+ Take a new snapshot first (`test-gen-gate.mjs baseline --out
140
+ "$RUN/write-baseline.json"`), then one subagent per platform, with the gaps,
141
+ discovery's conventions and the handles. With `ui_locator_codegen` (toolkit >= v3.25.0), locators come from the
142
+ UI tree node: `{platform, node}` returns the XCUITest or Compose code, or
143
+ `NO_HANDLE` with the handle to add, which goes back to stage 4. Without it,
144
+ write locators from the handles by hand. A locator is a handle, never a list
145
+ index or a visible string that is translated.
146
+
147
+ Each test asserts the gap's `expectation`. Test data comes from `secretRef` or
148
+ the fixtures the person supplied, never a literal secret. The subagent writes
149
+ `written.json`: `{files: [...], command: "<runs exactly these tests>",
150
+ tests: [{id, file}]}`. Then run the command once with a per-test report and:
151
+
152
+ ```bash
153
+ node "$HOME/.claude/scripts/test-gen-gate.mjs" written --repo "$REPO" \
154
+ --baseline "$RUN/write-baseline.json" --written "$RUN/written.json" \
155
+ --discovery "$RUN/discovery.json" --junit "$RUN/run-1.junit.xml"
156
+ ```
157
+
158
+ (`--xcresult-json` or `--log` where the runner writes no JUnit.) The gate fails
159
+ when the stage changed anything but test files, when a listed file is outside
160
+ the repository, missing, empty or not changed by the stage, when the stage's
161
+ diff carries test gaming (an environment branch, a swallowed error, an inflated
162
+ wait), when the command is none of the runners discovery found, or when the run
163
+ executed zero tests.
164
+
165
+ ## 6. Run
166
+
167
+ Run `written.json`'s command `testStability.repeatCount` times (default 3),
168
+ each run's log and per-test report kept as `run-<k>.log` and
169
+ `run-<k>.junit.xml` (or `.xcresult`). A UI suite goes through
170
+ `run-ui-tests.sh run`, whose `UI_TEST_RESULT` names the report. Each log must
171
+ clear `evidence-gate.mjs --claim test --status passed` before a pass is
172
+ recorded; a failing run is evidence too and is kept.
173
+
174
+ ## 7. Classify
175
+
176
+ `features/test-classification.md`: `test_results_classify` with run 1 as
177
+ `head`, the others as `confirm`, and the project's flake ledger, then
178
+ `test-classify.mjs merge`; without the tool, `test-classify.mjs fallback` over
179
+ the runs. Then:
180
+
181
+ ```bash
182
+ node "$HOME/.claude/scripts/test-gen-gate.mjs" results --classification "$RUN/test-classification.json" \
183
+ --written "$RUN/written.json" --out "$RUN/results.json"
184
+ node "$HOME/.claude/scripts/test-gen-gate.mjs" check --stage results --file "$RUN/results.json"
185
+ ```
186
+
187
+ `results.json` is three-state: `pass`, `fail` (a regression, an unstable test, a
188
+ preexisting failure), `not_measured` (no base, a setup failure); every non-pass
189
+ carries its reason. `preexisting` is a fail here although Gate 3 inherits it: the
190
+ test is new, so red on the base too means the behaviour it asserts is missing,
191
+ which is a finding for triage, not someone else's failure.
192
+
193
+ ## 8. Triage
194
+
195
+ Each `fail` and `not_measured` gets one verdict (the test-result-triage skill,
196
+ when enabled): `product-bug`, `stale-test`, `new-scenario`, `valid` or
197
+ `ambiguous`, and `ambiguous` is the default. `stale-test` needs the `file:line`
198
+ in the diff that changed the behaviour on purpose; a locator that found nothing
199
+ goes back to stage 4 before it may be called stale. A green test whose subject
200
+ changed behaviour is questioned too. Record each verdict, then:
201
+
202
+ ```bash
203
+ node "$HOME/.claude/scripts/test-gen-state.mjs" revise --state "$TG_STATE" --run "$RUN_ID" \
204
+ --test "<id>" --verdict "<verdict>" ${AUTOPILOT:+--autopilot}
205
+ ```
206
+
207
+ | Action | Then |
208
+ |---|---|
209
+ | `apply` | revise the test, rerun from stage 6 for that test |
210
+ | `park` | autopilot stops, because the verdict is `product-bug` (never applied without a person) or the stale-test cap (`prefs.global.testGen.revisionCap`, per run and per day) is reached. Report and end |
211
+ | `ask` | attended `product-bug`: show the evidence and ask whether to open it through `/multi-agent` |
212
+ | `none` | keep the verdict in `results.json` |
213
+
214
+ The count is read and written under a lock, so parallel triage cannot pass the
215
+ cap. A state or prefs file that does not parse is never counted as empty:
216
+ autopilot parks (exit 3), an attended run stops with exit 4 and records nothing.
217
+
218
+ ## End: the change base
219
+
220
+ After the report, record where this run ended so the next one diffs from here:
221
+
222
+ ```bash
223
+ node "$HOME/.claude/scripts/test-gen-state.mjs" base-record --state "$TG_STATE" --repo "$REPO" --run "$RUN_ID"
224
+ ```
225
+
226
+ The reason names the run (`test-gen run <id> finished`). It records which code
227
+ the tests were written against, not that the app is understood.
@@ -106,6 +106,9 @@
106
106
  },
107
107
  "resume": {
108
108
  "autoFix": false
109
+ },
110
+ "testGen": {
111
+ "revisionCap": 20
109
112
  }
110
113
  },
111
114
  "projects": {},
@@ -1123,6 +1123,20 @@
1123
1123
  }
1124
1124
  }
1125
1125
  },
1126
+ "testGen": {
1127
+ "type": "object",
1128
+ "additionalProperties": false,
1129
+ "description": "/multi-agent:test-gen knobs. Read by test-gen-state.mjs revise.",
1130
+ "properties": {
1131
+ "revisionCap": {
1132
+ "type": "integer",
1133
+ "minimum": 0,
1134
+ "maximum": 500,
1135
+ "default": 20,
1136
+ "description": "Stale-test revisions autopilot may apply, counted per run and per calendar day (UTC), whichever is reached first. At the cap the run parks instead of editing more tests. 0 parks on the first one."
1137
+ }
1138
+ }
1139
+ },
1126
1140
  "testStability": {
1127
1141
  "type": "object",
1128
1142
  "additionalProperties": false,
@@ -0,0 +1,73 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/mmerterden/multi-agent-pipeline/pipeline/schemas/test-gen-discovery.schema.json",
4
+ "title": "Multi-Agent Pipeline - test-gen discovery",
5
+ "description": "What the repository already has for testing the selected inventory items, $RUN/discovery.json of /multi-agent:test-gen (multi-agent-refs/test-gen/stages.md). Returned by the read-only test-gen-analyst persona and written by the orchestrator. testRoots[].command is what the write stage's run command is checked against (test-gen-gate.mjs written --discovery). Existing tests are joined to items by the identifier the test uses, never by a name. test-gen-gate.mjs check --stage discovery checks this shape without a dependency.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["stage", "testRoots", "existing", "handles"],
9
+ "$defs": {
10
+ "fileLine": { "type": "string", "pattern": "^[^\\s:]+:[1-9][0-9]*$" }
11
+ },
12
+ "properties": {
13
+ "stage": { "const": "discovery" },
14
+ "testRoots": {
15
+ "type": "array",
16
+ "items": {
17
+ "type": "object",
18
+ "additionalProperties": false,
19
+ "required": ["path", "runner", "command"],
20
+ "properties": {
21
+ "path": { "type": "string", "minLength": 1 },
22
+ "runner": {
23
+ "enum": [
24
+ "xcuitest",
25
+ "xctest",
26
+ "swift-testing",
27
+ "espresso",
28
+ "compose",
29
+ "junit",
30
+ "playwright",
31
+ "cypress",
32
+ "jest",
33
+ "vitest",
34
+ "pytest",
35
+ "other"
36
+ ]
37
+ },
38
+ "command": { "type": "string", "minLength": 1 },
39
+ "report": { "enum": ["junit", "xcresult", "none"] }
40
+ }
41
+ }
42
+ },
43
+ "existing": {
44
+ "type": "array",
45
+ "items": {
46
+ "type": "object",
47
+ "additionalProperties": false,
48
+ "required": ["inventoryId", "tests"],
49
+ "properties": {
50
+ "inventoryId": { "type": "string", "minLength": 1 },
51
+ "tests": { "type": "array", "items": { "$ref": "#/$defs/fileLine" } },
52
+ "joinedBy": { "type": "string", "minLength": 1 }
53
+ }
54
+ }
55
+ },
56
+ "handles": {
57
+ "type": "array",
58
+ "items": {
59
+ "type": "object",
60
+ "additionalProperties": false,
61
+ "required": ["inventoryId", "file", "line", "handle"],
62
+ "properties": {
63
+ "inventoryId": { "type": "string", "minLength": 1 },
64
+ "file": { "type": "string", "minLength": 1 },
65
+ "line": { "type": "integer", "minimum": 1 },
66
+ "handle": { "type": "string", "minLength": 1 }
67
+ }
68
+ }
69
+ },
70
+ "conventions": { "type": "array", "items": { "type": "string", "minLength": 1 } },
71
+ "notes": { "type": "array", "items": { "type": "string" } }
72
+ }
73
+ }
@@ -0,0 +1,122 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/mmerterden/multi-agent-pipeline/pipeline/schemas/test-gen-gaps.schema.json",
4
+ "title": "Multi-Agent Pipeline - test-gen gaps",
5
+ "description": "The gap analysis of /multi-agent:test-gen, $RUN/gaps.json (multi-agent-refs/test-gen/stages.md). Written from the read-only analyst's answer: the selected inventory items set against the existing tests, each gap a scenario to write. `wrongly-covered` (a test exists but cannot see the failure) is kept apart from `missing`. dataNeeds[] says what test data a scenario needs and who provides it; it never holds a value, because values are entered by a person or resolved by secret reference at run time. unmeasured[] lists what the analysis could not measure, which is not the same as covered. test-gen-gate.mjs check --stage gaps checks this shape without a dependency.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schemaVersion", "coverage", "gaps", "dataNeeds", "unmeasured"],
9
+ "$defs": {
10
+ "fileLine": { "type": "string", "pattern": "^[^\\s:]+:[1-9][0-9]*$" }
11
+ },
12
+ "properties": {
13
+ "schemaVersion": { "const": "1.0.0" },
14
+ "coverage": {
15
+ "type": "object",
16
+ "additionalProperties": false,
17
+ "required": ["method", "measured"],
18
+ "properties": {
19
+ "method": {
20
+ "enum": ["coverage_gap", "agent"],
21
+ "description": "coverage_gap: the toolkit tool joined inventory and tests by identifier; agent: the analyst's own reading, on a toolkit without the tool."
22
+ },
23
+ "measured": { "type": "boolean" },
24
+ "reason": { "type": "string", "minLength": 1 },
25
+ "covered": { "type": "integer", "minimum": 0 },
26
+ "uncovered": { "type": "integer", "minimum": 0 },
27
+ "unmatched": { "type": "integer", "minimum": 0 }
28
+ }
29
+ },
30
+ "gaps": {
31
+ "type": "array",
32
+ "items": {
33
+ "type": "object",
34
+ "additionalProperties": false,
35
+ "required": [
36
+ "id",
37
+ "inventoryId",
38
+ "bucket",
39
+ "title",
40
+ "priority",
41
+ "rationale",
42
+ "expectation",
43
+ "automation",
44
+ "method"
45
+ ],
46
+ "properties": {
47
+ "id": { "type": "string", "minLength": 1 },
48
+ "inventoryId": { "type": "string", "minLength": 1 },
49
+ "bucket": { "enum": ["missing", "wrongly-covered"] },
50
+ "title": { "type": "string", "minLength": 1 },
51
+ "priority": {
52
+ "enum": ["bug-catching", "edge", "basic"],
53
+ "description": "bug-catching: would catch a failure users hit; edge: a boundary or error path; basic: the happy path."
54
+ },
55
+ "rationale": {
56
+ "type": "string",
57
+ "minLength": 1,
58
+ "description": "Why the business cares."
59
+ },
60
+ "expectation": {
61
+ "type": "string",
62
+ "minLength": 1,
63
+ "description": "What the test asserts, measurable."
64
+ },
65
+ "automation": { "enum": ["yes", "no", "needs-code"] },
66
+ "method": {
67
+ "enum": ["exact", "inferred"],
68
+ "description": "exact: matched by identifier; inferred: the analyst's judgement, kept apart."
69
+ },
70
+ "evidence": { "type": "array", "items": { "$ref": "#/$defs/fileLine" } },
71
+ "handlesNeeded": {
72
+ "type": "array",
73
+ "items": {
74
+ "type": "object",
75
+ "additionalProperties": false,
76
+ "required": ["target", "suggestion"],
77
+ "properties": {
78
+ "target": { "type": "string", "minLength": 1 },
79
+ "suggestion": { "type": "string", "minLength": 1 }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ }
85
+ },
86
+ "dataNeeds": {
87
+ "type": "array",
88
+ "items": {
89
+ "type": "object",
90
+ "additionalProperties": false,
91
+ "required": ["id", "gapIds", "what", "kind"],
92
+ "properties": {
93
+ "id": { "type": "string", "minLength": 1 },
94
+ "gapIds": {
95
+ "type": "array",
96
+ "minItems": 1,
97
+ "items": { "type": "string", "minLength": 1 }
98
+ },
99
+ "what": { "type": "string", "minLength": 1 },
100
+ "kind": { "enum": ["account", "seed", "fixture", "secret", "other"] },
101
+ "secretRef": {
102
+ "type": "string",
103
+ "minLength": 1,
104
+ "description": "Credential name a test resolves at run time; never the value."
105
+ }
106
+ }
107
+ }
108
+ },
109
+ "unmeasured": {
110
+ "type": "array",
111
+ "items": {
112
+ "type": "object",
113
+ "additionalProperties": false,
114
+ "required": ["reason"],
115
+ "properties": {
116
+ "inventoryId": { "type": "string", "minLength": 1 },
117
+ "reason": { "type": "string", "minLength": 1 }
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,59 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/mmerterden/multi-agent-pipeline/pipeline/schemas/test-gen-inventory.schema.json",
4
+ "title": "Multi-Agent Pipeline - test-gen inventory",
5
+ "description": "What the app has that a test could cover, written by the inventory stage of /multi-agent:test-gen as $RUN/inventory.json (multi-agent-refs/test-gen/stages.md). Items are joined to tests by their stable id (route, screen id, Figma node id, endpoint path), never by title. `priority` is added after the inventory, from the user's pick or, in autopilot, the top three. test-gen-gate.mjs check --stage inventory checks this shape without a dependency.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schemaVersion", "platform", "source", "items"],
9
+ "properties": {
10
+ "schemaVersion": { "const": "1.0.0" },
11
+ "platform": { "enum": ["ios", "android", "web", "api"] },
12
+ "repo": { "type": "string", "minLength": 1 },
13
+ "source": {
14
+ "enum": ["design_scenario_inventory", "web_map", "ui_tree", "openapi", "agent"],
15
+ "description": "Where the items came from. `agent` is the stage's own reading of the code, used when no tool answered."
16
+ },
17
+ "generatedAt": { "type": "string", "format": "date-time" },
18
+ "items": {
19
+ "type": "array",
20
+ "minItems": 1,
21
+ "items": {
22
+ "type": "object",
23
+ "additionalProperties": false,
24
+ "required": ["id", "kind", "title"],
25
+ "properties": {
26
+ "id": { "type": "string", "minLength": 1, "description": "Stable identifier." },
27
+ "kind": { "enum": ["screen", "flow", "endpoint", "component"] },
28
+ "title": { "type": "string", "minLength": 1 },
29
+ "path": {
30
+ "type": "string",
31
+ "minLength": 1,
32
+ "description": "Code path that implements it, file or directory."
33
+ },
34
+ "handles": {
35
+ "type": "array",
36
+ "items": { "type": "string", "minLength": 1 },
37
+ "description": "Automation handles already on it (accessibilityIdentifier, testTag, data-testid)."
38
+ }
39
+ }
40
+ }
41
+ },
42
+ "priority": {
43
+ "type": "object",
44
+ "additionalProperties": false,
45
+ "required": ["mode", "selected"],
46
+ "properties": {
47
+ "mode": {
48
+ "enum": ["user", "autopilot-top3"],
49
+ "description": "user: picked by the person; autopilot-top3: the three the run proceeded with unattended."
50
+ },
51
+ "selected": {
52
+ "type": "array",
53
+ "minItems": 1,
54
+ "items": { "type": "string", "minLength": 1 }
55
+ }
56
+ }
57
+ }
58
+ }
59
+ }
@@ -0,0 +1,44 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/mmerterden/multi-agent-pipeline/pipeline/schemas/test-gen-results.schema.json",
4
+ "title": "Multi-Agent Pipeline - test-gen results",
5
+ "description": "The outcome of the tests /multi-agent:test-gen wrote, $RUN/results.json, built by test-gen-gate.mjs results from the classify stage (multi-agent-refs/features/test-classification.md). Three states: pass, fail, not_measured; anything but pass carries its reason. `classification` keeps the measured class behind the state (an unstable test is a fail, a test with no base is not_measured), and `triage` the result-triage verdict once one is made. test-gen-gate.mjs check --stage results checks this shape without a dependency.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": ["schemaVersion", "source", "green", "tests", "summary"],
9
+ "properties": {
10
+ "schemaVersion": { "const": "1.0.0" },
11
+ "source": { "enum": ["toolkit", "fallback"] },
12
+ "green": { "type": "boolean" },
13
+ "tests": {
14
+ "type": "array",
15
+ "items": {
16
+ "type": "object",
17
+ "additionalProperties": false,
18
+ "required": ["id", "status"],
19
+ "properties": {
20
+ "id": { "type": "string", "minLength": 1 },
21
+ "file": { "type": "string", "minLength": 1 },
22
+ "status": { "enum": ["pass", "fail", "not_measured"] },
23
+ "classification": {
24
+ "enum": ["passed", "regression", "preexisting", "unstable", "not_measured"]
25
+ },
26
+ "reason": { "type": "string", "minLength": 1 },
27
+ "triage": { "enum": ["product-bug", "stale-test", "new-scenario", "valid", "ambiguous"] }
28
+ },
29
+ "if": { "properties": { "status": { "enum": ["fail", "not_measured"] } } },
30
+ "then": { "required": ["reason"] }
31
+ }
32
+ },
33
+ "summary": {
34
+ "type": "object",
35
+ "additionalProperties": false,
36
+ "required": ["pass", "fail", "not_measured"],
37
+ "properties": {
38
+ "pass": { "type": "integer", "minimum": 0 },
39
+ "fail": { "type": "integer", "minimum": 0 },
40
+ "not_measured": { "type": "integer", "minimum": 0 }
41
+ }
42
+ }
43
+ }
44
+ }