mmt-testlight 0.4.4 → 0.4.5

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.
@@ -0,0 +1,167 @@
1
+ This file tells the AI how to generate **`type: suite`** `.mmt` files.
2
+
3
+ Always follow these rules:
4
+ - Output must be valid YAML.
5
+ - The first non-comment line must be `type: suite`.
6
+ - Suites should primarily **list other mmt files** (tests, APIs, or other suites) to be executed.
7
+
8
+ ---
9
+
10
+ ## Schema (mental model for the AI)
11
+
12
+ These fields match `SuiteData` in `core/src/SuiteData.ts` and the public docs in `docs/files/suite/index.md`.
13
+
14
+ Top-level keys and types:
15
+
16
+ ```yaml
17
+ type: suite # REQUIRED, must be exactly "suite"
18
+
19
+ title: string # optional, human-readable suite name
20
+ tags: # optional, for grouping
21
+ - string
22
+
23
+ description: string # optional, short explanation
24
+
25
+ servers: # optional, root-only: mock servers started before items
26
+ - path/to/server.mmt # type: server files; kept running for the entire suite
27
+
28
+ items: # REQUIRED, array of files to run
29
+ - path/to/file1.mmt
30
+ - path/to/file2.mmt
31
+ - then
32
+ - path/to/file3.mmt
33
+
34
+ environment: # optional, root-only: configure env for suite runs
35
+ preset?: string # preset name from multimeter.mmt (or from file)
36
+ file?: string # path to an env file to load
37
+ variables?: # inline key-value environment variables
38
+ <name>: <value>
39
+
40
+ export: # optional, root-only: generate reports after completion
41
+ - path/to/report.xml # supported: .xml (JUnit), .html, .md, .mmt
42
+ ```
43
+
44
+ > **Root-only fields**: `servers`, `environment`, and `export` only take effect when the suite is run directly. When imported by another suite, these fields are ignored.
45
+
46
+ > **Legacy alias**: `tests` is still accepted as an alias for `items`, but new suites should use `items`.
47
+
48
+ ---
49
+
50
+ ## Execution Flow (`items` array)
51
+
52
+ The `items` array defines the execution flow.
53
+ - All files listed between `then` separators (or before the first one) are run in parallel.
54
+ - The groups of files separated by `then` are run sequentially.
55
+ - **Server files** (`type: server`) can be included — they start before items in the same stage and stop when the suite completes.
56
+
57
+ Example: `[a, b, then, c]` will run `a` and `b` in parallel, and once both are finished, it will run `c`.
58
+
59
+ ---
60
+
61
+ ## Common patterns the AI should generate
62
+
63
+ ### 1. Simple suite running all items in parallel
64
+
65
+ User asks: "Create a suite to run smoke tests `test1.mmt` and `test2.mmt`."
66
+
67
+ ```yaml
68
+ type: suite
69
+ title: Smoke Tests
70
+ tags: [smoke]
71
+ items:
72
+ - ./tests/test1.mmt
73
+ - ./tests/test2.mmt
74
+ ```
75
+
76
+ ### 2. Sequential execution of items
77
+
78
+ User asks: "Create a suite that first runs `login.mmt`, and then `get_user.mmt`."
79
+
80
+ ```yaml
81
+ type: suite
82
+ title: Login and Get User
83
+ tags: [smoke, auth]
84
+ items:
85
+ - ./tests/login.mmt
86
+ - then
87
+ - ./tests/get_user.mmt
88
+ ```
89
+
90
+ ### 3. Mixed parallel and sequential execution
91
+
92
+ User asks: "Create a suite that runs `test1.mmt` and `test2.mmt` in parallel, and after they are done, runs `test3.mmt`."
93
+
94
+ ```yaml
95
+ type: suite
96
+ title: Mixed Execution Suite
97
+ tags: [regression]
98
+ items:
99
+ - ./tests/test1.mmt
100
+ - ./tests/test2.mmt
101
+ - then
102
+ - ./tests/test3.mmt
103
+ ```
104
+
105
+ ### 4. Suite with mock servers
106
+
107
+ User asks: "Create a suite that starts a mock server before running integration tests."
108
+
109
+ ```yaml
110
+ type: suite
111
+ title: Integration with Mock Server
112
+ tags: [integration]
113
+ items:
114
+ - ./mocks/user-service.mmt # type: server — starts first
115
+ - then
116
+ - ./tests/user_crud.mmt
117
+ - ./tests/user_auth.mmt
118
+ ```
119
+
120
+ Servers start before items in the same stage and stop when the suite finishes.
121
+
122
+ ### 5. Suite with top-level servers and environment
123
+
124
+ User asks: "Create an integration suite with a mock server, using the staging preset."
125
+
126
+ ```yaml
127
+ type: suite
128
+ title: Integration Suite
129
+ tags: [integration]
130
+ servers:
131
+ - ./mocks/user-service.mmt
132
+ - ./mocks/auth-service.mmt
133
+ environment:
134
+ preset: staging
135
+ items:
136
+ - ./tests/login.mmt
137
+ - ./tests/profile.mmt
138
+ ```
139
+
140
+ Using the `servers` field is recommended over placing servers in `items` — it keeps them running for the entire suite and avoids ordering issues.
141
+
142
+ ### 6. Suite with report exports
143
+
144
+ User asks: "Create a CI suite that exports JUnit XML and HTML reports."
145
+
146
+ ```yaml
147
+ type: suite
148
+ title: CI Suite
149
+ export:
150
+ - ./reports/results.xml
151
+ - ./reports/results.html
152
+ items:
153
+ - ./tests/login.mmt
154
+ - ./tests/profile.mmt
155
+ ```
156
+
157
+ ---
158
+
159
+ ## Style rules for the AI
160
+
161
+ - Use 2-space indentation.
162
+ - Prefer clear and descriptive titles.
163
+ - Always include a `title`.
164
+ - Use `tags` to categorize suites where appropriate.
165
+ - Ensure file paths in the `items` array are plausible.
166
+
167
+ When unsure, generate a **minimal valid suite** that includes the requested files.
@@ -0,0 +1,60 @@
1
+ # Auto Test Generation Skill
2
+
3
+ Use this skill when the user wants a Multimeter test generated from an API file, an OpenAPI/Postman spec, or a natural-language scenario.
4
+
5
+ ## Goal
6
+
7
+ Produce a valid Multimeter `type: test` YAML file that is deterministic, minimal, and easy to validate — with **low token cost**.
8
+
9
+ ## Required workflow (API → test)
10
+
11
+ 1. Optional few-shot: `list_examples` → mirror **`goldenSmoke`** (`examples/ai/golden_smoke/`).
12
+ 2. Identify the target API `.mmt` path (`discover_api` / `api_card` only if needed).
13
+ 3. Call MCP **`scaffold_test({ workspaceRoot, apiPath })`** — **required**. Do not invent a blank test.
14
+ - Offline: `testlight scaffold test --from <api.mmt>`
15
+ 4. Write the returned YAML to `suggestedPath` (or the user path).
16
+ 5. Apply **only minimal** edits (title, expects, inputs). Prefer smoke unless asked for more.
17
+ 6. **`validate` until valid, then `format`** — both required before finishing.
18
+ 7. Stop — no polish rewrite.
19
+ 8. If the user wants stronger asserts after a run: **`suggest_assertions` → patch → validate → format**.
20
+
21
+ ## Modify discipline
22
+
23
+ - **Patch only.** Never replace the whole file unless the user explicitly says rewrite/regenerate.
24
+ - Prefer surgical edits (one step, one expect, one input).
25
+
26
+ ## Rules
27
+
28
+ - Output only valid YAML for the generated test.
29
+ - Start the file with `type: test`.
30
+ - Never add YAML comments (`#`).
31
+ - Never web-search Multimeter syntax.
32
+ - Use tokens such as `e:`, `i:`, `r:`, and `c:` when appropriate.
33
+ - If the source is ambiguous, ask a short clarifying question before generating.
34
+
35
+ ## Golden smoke shape (mirror this)
36
+
37
+ See `docs/AI/golden-smoke.md` / `list_examples.goldenSmoke`. Scaffold already produces:
38
+
39
+ ```yaml
40
+ type: test
41
+ title: <API title> smoke test
42
+ tags: [smoke, ...]
43
+ import:
44
+ <alias>: <relative-api-path>
45
+ inputs:
46
+ <name>: i:<name>
47
+ steps:
48
+ - call: <alias>
49
+ id: <step_id>
50
+ inputs:
51
+ <name>: i:<name>
52
+ expect:
53
+ status: 200
54
+ ```
55
+
56
+ ## When to use this skill
57
+
58
+ - “Generate a test for this API.”
59
+ - “Create a smoke test from this OpenAPI spec.”
60
+ - “Turn this endpoint into a Multimeter test.”
@@ -0,0 +1,335 @@
1
+ This file tells the AI how to generate **`type: test`** `.mmt` files.
2
+
3
+ **Prefer MCP `scaffold_test` (or `testlight scaffold test --from`) for new tests from an API.** Use this guide for fields and step types beyond the scaffold baseline — not as an excuse to invent a full file from scratch.
4
+
5
+ Always follow these rules:
6
+ - Output must be valid YAML.
7
+ - The first non-comment line must be `type: test`.
8
+ - Tests should primarily **call APIs or other tests**, assert results, and optionally loop or branch.
9
+
10
+ ---
11
+
12
+ ## Schema (mental model for the AI)
13
+
14
+ These fields match `TestData` in `core/src/TestData.ts` and the public docs in `docs/files/test/index.md`.
15
+
16
+ Top-level keys and types:
17
+
18
+ ```yaml
19
+ type: test # REQUIRED, must be exactly "test"
20
+
21
+ title: string # REQUIRED, human-readable test name
22
+ tags: # REQUIRED (can be empty array) for grouping
23
+ - string
24
+
25
+ description: string # optional, short explanation
26
+
27
+ import: # alias -> file path (apis, tests, CSV)
28
+ <alias>: path/to/file.mmt
29
+
30
+ inputs: # input variables with default values
31
+ <name>: string # or number/boolean/null (JSON-compatible)
32
+
33
+ outputs: # top-level outputs for this test
34
+ <name>: string # expression or literal
35
+
36
+ cache: 5m # optional; when this test is imported+called again
37
+ # in the same root run with same title+inputs,
38
+ # reuse outputs (skip callee body). Duration like
39
+ # repeat/delay, epoch number, or datetime with ":"
40
+
41
+ steps: # linear flow of steps (no stages)
42
+ - <step>
43
+
44
+ stages: # parallel/grouped flows
45
+ - id: string
46
+ title?: string
47
+ condition?: string # optional condition string
48
+ after?: string | string[]
49
+ steps: # steps inside this stage
50
+ - <step>
51
+ ```
52
+
53
+ `steps` and `stages` are mutually exclusive in typical usage — use **one** or the other unless you have a very deliberate reason.
54
+
55
+ ---
56
+
57
+ ## Step types
58
+
59
+ All steps are objects with a `type` (implicit from the key). They map to `TestFlowStep` in `TestData.ts`.
60
+
61
+ Supported step forms:
62
+
63
+ ```yaml
64
+ # 1) call: invoke another test or an API (imported by alias)
65
+ - call: <alias>
66
+ id: <stepId> # REQUIRED, used to reference outputs later
67
+ title?: string # optional human-readable label
68
+ inputs?: # passed as JSON object
69
+ <name>: <value>
70
+ expect?: # inline assertions (shorthand for assert)
71
+ <field>: <value>
72
+ debug?: # inline debug inspections (same syntax as expect, never fails)
73
+ <field>: <value>
74
+ report?: all | fails | none # controls reporting level
75
+
76
+ # 2) run: start an imported mock server (type: server file)
77
+ - run: <alias> # alias of an imported server file
78
+
79
+ # 3) check: soft assertion (logs failure, continues)
80
+ - check: <comparison>
81
+
82
+ # 4) assert: hard assertion (stops on failure)
83
+ - assert: <comparison>
84
+
85
+ # 5) if: conditional block (optional else)
86
+ - if: <comparison>
87
+ steps:
88
+ - <step>
89
+ else:
90
+ - <step>
91
+
92
+ # 6) repeat: repeat a block N times or by string
93
+ - repeat: <number | string>
94
+ steps:
95
+ - <step>
96
+
97
+ # 7) for: loop header (JS-style or shorthand)
98
+ - for: <header or expression>
99
+ steps:
100
+ - <step>
101
+
102
+ # 8) js: inline JavaScript
103
+ - js: |
104
+ // javascript code
105
+
106
+ # 9) print: log a message
107
+ - print: "text"
108
+
109
+ # 10) delay: sleep
110
+ - delay: <number | string> # e.g. 200, "2s", "1m"
111
+
112
+ # 11) set: assign/update variables
113
+ - set:
114
+ <name>: <value>
115
+
116
+ # 12) var / const / let: JS-style declarations
117
+ - var:
118
+ <name>: <value>
119
+ - const:
120
+ <name>: <value>
121
+ - let:
122
+ <name>: <value>
123
+
124
+ # 13) data: bind CSV alias (from import)
125
+ - data: <alias>
126
+
127
+ # 14) setenv: promote values into environment variables
128
+ - setenv:
129
+ <env_name>: <value> # e.g. token: ${loginStep.token}
130
+ ```
131
+
132
+ ### Additional `call` fields
133
+
134
+ Beyond the basic `call`, `id`, and `inputs`, a call step also supports:
135
+
136
+ ```yaml
137
+ - call: <alias>
138
+ id: <stepId>
139
+ title?: string # optional human-readable label for logs/reports
140
+ inputs?: { ... }
141
+ expect?: # inline assertions on the call result
142
+ <field>: <value> # e.g. status: 200, body.name: "John"
143
+ debug?: # inline debug inspections (same syntax as expect, never fails)
144
+ <field>: <value> # e.g. status: 200, body: ${someVar}
145
+ report?: all | fails | none # controls pass/fail reporting level
146
+ ```
147
+
148
+ `expect` is a shorthand for common assertions — each key is a dotted path into the response, and the value is compared with `==` by default. You can prefix with an operator (e.g. `>= 1`, `!= null`).
149
+
150
+ `debug` uses the same syntax as `expect` but never fails the test — it always logs results with a debug icon for troubleshooting. Debug entries are excluded from exported reports.
151
+
152
+ ---
153
+
154
+ `<comparison>` is a string expression using operators from `opsList` in `TestData.ts`:
155
+
156
+ - `<`, `>`, `<=`, `>=`, `==`, `!=`
157
+ - `=@` (is in: left is contained in right), `!@` (is not in)
158
+ - `=C` (contains: left contains right), `!C` (does not contain)
159
+ - `=^` (starts with), `!^` (not starts with)
160
+ - `=$` (ends with), `!$` (not ends with)
161
+ - `=*` (regex match), `!*` (regex not match)
162
+ - `=~` (equal as string / type-unsafe), `!~` (not equal as string) — for XML/text string outputs vs bare YAML booleans/numbers
163
+ - `=#` (string/number character length equals), `!#` (not equal)
164
+ - `>N%`(fuzzy match at least N% similar), `<N%` (fuzzy match less than N%). Any whole percent from 0 to 100 can be used, for example `>80%`. In the visual UI these appear as `>%` and `<%` with a separate percentage selector.
165
+
166
+ Example comparisons:
167
+
168
+ ```yaml
169
+ - assert: ${login.status} == 200
170
+ - check: ${profile.email} =* /@example.com$/
171
+ - assert: ${response.body.total} >= 1
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Naming and references
177
+
178
+ - Every `call` step **must** have a unique `id` within the test.
179
+ - Use that `id` to reference its outputs in later steps (the exact JS shape comes from the imported file, but following patterns from the docs is enough):
180
+
181
+ ```yaml
182
+ - call: login
183
+ id: loginStep
184
+ inputs:
185
+ username: i:username
186
+ password: i:password
187
+
188
+ - assert: ${loginStep.status} == 200
189
+ ```
190
+
191
+ - Use `set` to place values into `outputs` if you want the test itself to expose results:
192
+
193
+ ```yaml
194
+ - set:
195
+ outputs.token: ${loginStep.token}
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Tokens the AI can use in tests
201
+
202
+ You can use the same token syntaxes as for APIs:
203
+
204
+ - Environment `variables:` `e:api_url`, `e:auth_token`, etc., or `<<e:api_url>>`
205
+ - Test `inputs:` `i:user_id` or `<<i:user_id>>`
206
+ - Random values: `r:name` or `<<r:name>>`
207
+ - Current/time values: `c:name` or `<<c:name>>`
208
+
209
+ Guidelines:
210
+ - Use bare tokens when they are the **entire value**.
211
+ - Use `<< >>` when embedding in text.
212
+
213
+ Example:
214
+
215
+ ```yaml
216
+ inputs:
217
+ username: user@example.com
218
+ password: string
219
+
220
+ steps:
221
+ - call: login
222
+ id: loginStep
223
+ inputs:
224
+ username: i:username
225
+ password: i:password
226
+ - assert: ${loginStep.status} == 200
227
+ - set:
228
+ outputs.token: ${loginStep.token}
229
+ ```
230
+
231
+ ---
232
+
233
+ ## Common patterns the AI should generate
234
+
235
+ ### 1. Simple smoke test for a single API
236
+
237
+ User asks: "Create a test that logs in and checks status 200. The API alias is `login`."
238
+
239
+ ```yaml
240
+ type: test
241
+ title: Login smoke test
242
+ tags: [smoke, auth]
243
+ inputs:
244
+ username: user@example.com
245
+ password: string
246
+ steps:
247
+ - call: login
248
+ id: loginStep
249
+ inputs:
250
+ username: i:username
251
+ password: i:password
252
+ - assert: ${loginStep.status} == 200
253
+ ```
254
+
255
+ ### 2. Chained calls: login then get profile
256
+
257
+ ```yaml
258
+ type: test
259
+ title: Login and get profile
260
+ tags: [smoke, auth, profile]
261
+ import:
262
+ login: ./login.mmt
263
+ get_profile: ./get_profile.mmt
264
+ inputs:
265
+ username: user@example.com
266
+ password: string
267
+ steps:
268
+ - call: login
269
+ id: loginStep
270
+ inputs:
271
+ username: i:username
272
+ password: i:password
273
+ - assert: ${loginStep.status} == 200
274
+
275
+ - call: get_profile
276
+ id: profileStep
277
+ inputs:
278
+ token: ${loginStep.token}
279
+ - assert: ${profileStep.status} == 200
280
+ ```
281
+
282
+ ### 3. Data-driven loop over CSV
283
+
284
+ ```yaml
285
+ type: test
286
+ title: Login for multiple users
287
+ tags: [load, auth]
288
+ import:
289
+ users: ./users.csv
290
+ login: ./login.mmt
291
+
292
+ steps:
293
+ - data: users
294
+ - for: const user of users
295
+ steps:
296
+ - call: login
297
+ id: loginStep
298
+ inputs:
299
+ username: ${user.username}
300
+ password: ${user.password}
301
+ - check: ${loginStep.status} == 200
302
+ ```
303
+
304
+ ### 4. Conditional flows
305
+
306
+ ```yaml
307
+ type: test
308
+ title: Conditional retry on failure
309
+ tags: [resilience]
310
+ import:
311
+ login: ./login.mmt
312
+
313
+ steps:
314
+ - call: login
315
+ id: login1
316
+ - if: ${login1.status} != 200
317
+ steps:
318
+ - print: "Retrying login"
319
+ - call: login
320
+ id: login2
321
+ - assert: ${login2.status} == 200
322
+ else:
323
+ - print: "Login succeeded on first attempt"
324
+ ```
325
+
326
+ ---
327
+
328
+ ## Style rules for the AI
329
+
330
+ - Use 2-space indentation.
331
+ - Prefer small, readable tests focused on a single behavior.
332
+ - Prefer `steps` for simple linear flows; only use `stages` when explicit parallelism is needed.
333
+ - Always include `title` and `tags` (even if tags is a small list like `[smoke]`).
334
+ - Avoid adding `js` steps unless the user needs custom logic that can’t be expressed with other constructs. YAML comments (`#`) are fine — Format Document preserves them. Prefer `title` / `description` for structured documentation.
335
+ When unsure, generate a **minimal valid test** that clearly calls the described APIs and asserts the most important property (typically HTTP status or a key field in the response).
@@ -0,0 +1,60 @@
1
+ This file is the **single entry point** when the AI needs to **generate or modify** any `.mmt` file.
2
+
3
+ **Copilot / MCP:** Before editing YAML, call `read_documentation` for the file type, then `validate` after every edit. See `AI/agent-workflow.md`.
4
+
5
+ The AI must:
6
+ - Always output valid **YAML**.
7
+ - Always put `type` as the **first key** in the file.
8
+ - Choose the correct `type` based on the user request (rules below).
9
+ - Use the exact structures outlined and never deviate them.
10
+ - Use the fence language identifier yaml (i.e. ```yaml).
11
+ - Do not use stray backticks inside YAML.
12
+ - YAML comments (`#`) are preserved by Format Document. Prefer `description` / `title` for structured docs that also survive UI edits.
13
+
14
+ The only file types are:
15
+ - `type: api` – describe a single HTTP/WebSocket endpoint.
16
+ - `type: test` – describe a test flow that calls APIs/tests.
17
+ - `type: env` – define environment variables and presets.
18
+ - `type: doc` – describe API documentation over a set of `.mmt` files.
19
+ - `type: suite` – group and run multiple tests, APIs, or other suites.
20
+ - `type: loadtest` – run one `type: test` file with load configuration.
21
+ - `type: server` – define a mock server with endpoints, matching, and responses.
22
+ - `type: report` – structured test results generated by runs (viewable in the editor; usually not hand-authored).
23
+
24
+ When the user asks you to **create or change** a `.mmt` file, follow this order:
25
+
26
+ 1. **Decide the type**
27
+ - If the user talks about an endpoint, URL, method, headers, body, or examples → use `type: api`.
28
+ - If the user talks about scenarios, flows, assertions, retries, loops, or "tests" → use `type: test`.
29
+ - If the user talks about base URLs, credentials, modes, feature flags, presets → use `type: env`.
30
+ - If the user talks about documentation, catalogs, grouping APIs → use `type: doc`.
31
+ - If the user talks about running multiple tests, orchestration, collections of tests → use `type: suite`.
32
+ - If the user talks about concurrency, ramp-up, repeated execution, or load against one test flow → use `type: loadtest`.
33
+ - If the user talks about mocking, fake servers, stubbing responses, or "mock server" → use `type: server`.
34
+ - If the user talks about test reports, CI results, or exporting run results → `type: report` files are generated automatically. Explain the `--report` flag (see `docs/files/report/index.md`) rather than generating a report file.
35
+
36
+ 2. **Jump to the detailed generator**
37
+ - For `type: api`, use **`AI/generate-api.md`** to shape fields and examples.
38
+ - For `type: test`, use **`AI/generate-test.md`**.
39
+ - For `type: env`, use **`AI/generate-env.md`**.
40
+ - For `type: doc`, use **`AI/generate-doc.md`**.
41
+ - For `type: suite`, use **`AI/generate-suite.md`**.
42
+ - For `type: loadtest`, use **`AI/generate-loadtest.md`**.
43
+
44
+ 3. **Token syntax (always snake_case)**
45
+ - Environment `variables:` `e:api_url`, `e:auth_token`, etc.
46
+ - Embedded in strings/URLs: `<<e:api_url>>/users`, `Bearer <<e:auth_token>>`.
47
+ - Test/API `inputs:` `i:user_id`, `<<i:user_id>>`.
48
+ - Random: `r:uuid`, `r:first_name`, etc.
49
+ - Current/time: `c:date`, `c:epoch_ms`, etc.
50
+
51
+ Rules:
52
+ - Prefer **snake_case** for names inside tokens: `e:api_url`, `i:user_id`, not `e:api_url`.
53
+ - Use bare tokens as the **entire YAML value** when possible.
54
+ - Use `<< >>` only when embedding tokens inside a larger string.
55
+
56
+ 4. **Editing vs. creating**
57
+ - When creating a new `file:` start from the minimal valid skeleton in the corresponding `generate-*.md`.
58
+ - When editing: keep the existing structure, only add/change what the user asked, and do not remove unrelated fields.
59
+
60
+ If you only need to **reason** about a user file (and not generate new YAML), prefer reading **only the relevant `AI/generate-*.md`** file to save tokens.
@@ -0,0 +1,52 @@
1
+ # Golden smoke pair (AI)
2
+
3
+ Canonical low-token example. Mirror this shape; prefer `scaffold_test` over inventing YAML.
4
+
5
+ ## API — `examples/ai/golden_smoke/apis/echo.mmt`
6
+
7
+ ```yaml
8
+ type: api
9
+ title: Echo message
10
+ description: Golden smoke API for AI agents — minimal POST with outputs
11
+ method: post
12
+ url: https://test.mmt.dev/echo
13
+ format: json
14
+ inputs:
15
+ message: hello
16
+ outputs:
17
+ echoed: body.message
18
+ body:
19
+ message: i:message
20
+ ```
21
+
22
+ ## Test — `examples/ai/golden_smoke/tests/echo-smoke.mmt`
23
+
24
+ Produced by `scaffold_test` / `testlight scaffold test --from` (do not hand-author from scratch):
25
+
26
+ ```yaml
27
+ type: test
28
+ title: Echo message smoke test
29
+ description: Smoke test for API "Echo message".
30
+ tags:
31
+ - smoke
32
+ - post
33
+ import:
34
+ echo: ../apis/echo.mmt
35
+ inputs:
36
+ message: i:message
37
+ steps:
38
+ - call: echo
39
+ id: iEcho
40
+ inputs:
41
+ message: i:message
42
+ expect:
43
+ status: 200
44
+ echoed: != null
45
+ ```
46
+
47
+ ## Agent rules
48
+
49
+ 1. New API test → `scaffold_test` first.
50
+ 2. Then `validate` → `format`.
51
+ 3. Modify → patch only (never full-file rewrite unless the user asks).
52
+ 4. After a run, to tighten asserts → `suggest_assertions` → patch → `validate` → `format`.
@@ -0,0 +1,29 @@
1
+ # Generate `type: api` (min)
2
+
3
+ ```yaml
4
+ type: api
5
+ title: Login
6
+ method: post
7
+ url: <<e:api_url>>/login
8
+ format: json
9
+ inputs:
10
+ username: i:username
11
+ password: i:password
12
+ body:
13
+ username: i:username
14
+ password: i:password
15
+ outputs:
16
+ token: body.token
17
+ ```
18
+
19
+ ## Essentials
20
+
21
+ - First line: `type: api`
22
+ - `url` required; `method` required for HTTP
23
+ - `protocol`: `http` or `ws` (often inferred)
24
+ - `format`: `json` | `text` | `xml` | `urlencoded` | …
25
+ - Optional: `headers`, `query`, `cookies`, `examples`, `setenv`
26
+ - Tokens: `e:`, `i:`, `r:`, `c:`
27
+
28
+ For tests against this API use `scaffold_test` / `api_card`, not a full OpenAPI dump.
29
+ Request `pack: full` for WS, GraphQL, gRPC, and rare fields.