devlanes 0.2.0 → 0.3.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.
package/schema.json CHANGED
@@ -4,52 +4,44 @@
4
4
  "ComposeFiles": {
5
5
  "anyOf": [
6
6
  {
7
- "description": "One Compose file relative to the project.",
8
- "examples": [
9
- "docker-compose.yml"
10
- ],
11
7
  "type": "string"
12
8
  },
13
9
  {
14
- "description": "Ordered Compose files, merged in the same order as repeated -f arguments.",
15
- "examples": [
16
- [
17
- "docker-compose.yml",
18
- "docker-compose.dev.yml"
19
- ]
20
- ],
21
10
  "items": {
22
- "description": "Ordered Compose files, merged in the same order as repeated -f arguments.",
11
+ "description": "Compose files merged in order, like repeated `-f` arguments.",
23
12
  "examples": [
24
13
  "docker-compose.yml",
25
14
  "docker-compose.dev.yml"
26
15
  ],
16
+ "markdownDescription": "Compose files merged in order, like repeated `-f` arguments.",
27
17
  "type": "string"
28
18
  },
29
19
  "type": "array"
30
20
  }
31
21
  ],
32
- "description": "Compose files used to resolve and start infrastructure.",
22
+ "description": "Compose file, or files merged like repeated `-f` arguments.",
33
23
  "examples": [
34
24
  "docker-compose.yml",
35
25
  [
36
26
  "docker-compose.yml",
37
27
  "docker-compose.dev.yml"
38
28
  ]
39
- ]
29
+ ],
30
+ "markdownDescription": "Compose file, or files merged like repeated `-f` arguments."
40
31
  },
41
32
  "ConfigDuration": {
42
- "description": "A duration written with `ms`, `s`, `m`, `h`, or `d` units.",
33
+ "description": "A duration: a whole number followed by `ms`, `s`, `m`, `h` or `d`, such as `500ms`, `15s` or `10m`. Units cannot be combined.",
43
34
  "examples": [
44
35
  "15s",
45
36
  "10m"
46
37
  ],
38
+ "markdownDescription": "A duration: a whole number followed by `ms`, `s`, `m`, `h` or `d`, such as `500ms`, `15s` or `10m`. Units cannot be combined.",
47
39
  "pattern": "^[0-9]+(ms|s|m|h|d)$",
48
40
  "type": "string"
49
41
  },
50
42
  "ContainerConfig": {
51
43
  "additionalProperties": false,
52
- "description": "Plain Docker container managed alongside Compose services.",
44
+ "description": "Docker container run without a Compose file, shared by every lane or one per lane. Processes reach it through `${{ infra.<name>.ports.<port> }}`.",
53
45
  "examples": [
54
46
  {
55
47
  "image": "ghcr.io/cerbos/cerbos:0.40.0",
@@ -60,98 +52,119 @@
60
52
  }
61
53
  }
62
54
  ],
55
+ "markdownDescription": "Docker container run without a Compose file, shared by every lane or one per lane. Processes reach it through `${{ infra.<name>.ports.<port> }}`.",
63
56
  "properties": {
64
57
  "command": {
65
58
  "anyOf": [
66
59
  {
67
- "$ref": "#/$defs/Run"
60
+ "$ref": "#/$defs/Run",
61
+ "description": "",
62
+ "examples": [],
63
+ "markdownDescription": ""
68
64
  },
69
65
  {
70
66
  "type": "null"
71
67
  }
72
68
  ],
73
- "description": "Command replacing the image's default command.",
69
+ "description": "Command replacing the image's default: a list is used as is, a string runs through `/bin/sh -c` inside the container.",
74
70
  "examples": [
75
71
  [
76
72
  "server",
77
73
  "--config=/policies/config.yaml"
78
74
  ]
79
- ]
75
+ ],
76
+ "markdownDescription": "Command replacing the image's default: a list is used as is, a string runs through `/bin/sh -c` inside the container."
80
77
  },
81
78
  "env": {
82
79
  "additionalProperties": {
83
80
  "$ref": "#/$defs/Templated_Scalar",
84
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
81
+ "description": "Environment variables set inside the container.",
85
82
  "examples": [
86
- "${{ lane.index }}"
87
- ]
83
+ "debug"
84
+ ],
85
+ "markdownDescription": "Environment variables set inside the container."
88
86
  },
89
87
  "default": {},
90
- "description": "Environment variables passed to this container.",
88
+ "description": "Environment variables set inside the container.",
91
89
  "examples": [
92
90
  {
93
91
  "LOG_LEVEL": "debug"
94
92
  }
95
93
  ],
94
+ "markdownDescription": "Environment variables set inside the container.",
96
95
  "type": "object"
97
96
  },
98
97
  "image": {
99
98
  "anyOf": [
100
99
  {
101
- "$ref": "#/$defs/Templated_string"
100
+ "$ref": "#/$defs/Templated_string",
101
+ "description": "",
102
+ "examples": [],
103
+ "markdownDescription": ""
102
104
  },
103
105
  {
104
106
  "type": "null"
105
107
  }
106
108
  ],
107
- "description": "Docker image to run; required after local overrides are merged.",
109
+ "description": "Image to run. Required. A running container with the same image, command, env, mounts and ports is reused; any change recreates it.",
108
110
  "examples": [
109
111
  "ghcr.io/cerbos/cerbos:0.40.0"
110
- ]
112
+ ],
113
+ "markdownDescription": "Image to run. Required. A running container with the same image, command, env, mounts and ports is reused; any change recreates it."
111
114
  },
112
115
  "per_lane": {
113
116
  "anyOf": [
114
117
  {
115
- "$ref": "#/$defs/Templated_boolean"
118
+ "$ref": "#/$defs/Templated_boolean",
119
+ "description": "",
120
+ "examples": [],
121
+ "markdownDescription": ""
116
122
  },
117
123
  {
118
124
  "type": "null"
119
125
  }
120
126
  ],
121
- "description": "Create one container per lane; defaults to a shared project container.",
127
+ "description": "Run one container per lane instead of one shared by every lane. Defaults to false. Per-lane containers and their named volumes are removed by `devlanes lane clean`.",
122
128
  "examples": [
123
129
  true,
124
130
  false
125
- ]
131
+ ],
132
+ "markdownDescription": "Run one container per lane instead of one shared by every lane. Defaults to false. Per-lane containers and their named volumes are removed by `devlanes lane clean`."
126
133
  },
127
134
  "ports": {
128
135
  "additionalProperties": {
129
136
  "$ref": "#/$defs/Templated_uint16",
130
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
137
+ "description": "Container ports by name. devlanes publishes each on a host port on 127.0.0.1, read as `${{ infra.<name>.ports.<port> }}`; `infra.<name>.port` is the first one.",
131
138
  "examples": [
132
- "${{ lane.index }}"
133
- ]
139
+ 3592,
140
+ 3593
141
+ ],
142
+ "markdownDescription": "Container ports by name. devlanes publishes each on a host port on 127.0.0.1, read as `${{ infra.<name>.ports.<port> }}`; `infra.<name>.port` is the first one."
134
143
  },
135
144
  "default": {},
136
- "description": "Named container ports; host ports are allocated by devlanes.",
145
+ "description": "Container ports by name. devlanes publishes each on a host port on 127.0.0.1, read as `${{ infra.<name>.ports.<port> }}`; `infra.<name>.port` is the first one.",
137
146
  "examples": [
138
147
  {
139
148
  "grpc": 3593,
140
149
  "http": 3592
141
150
  }
142
151
  ],
152
+ "markdownDescription": "Container ports by name. devlanes publishes each on a host port on 127.0.0.1, read as `${{ infra.<name>.ports.<port> }}`; `infra.<name>.port` is the first one.",
143
153
  "type": "object"
144
154
  },
145
155
  "ready": {
146
156
  "anyOf": [
147
157
  {
148
- "$ref": "#/$defs/ReadyConfig"
158
+ "$ref": "#/$defs/ReadyConfig",
159
+ "description": "",
160
+ "examples": [],
161
+ "markdownDescription": ""
149
162
  },
150
163
  {
151
164
  "type": "null"
152
165
  }
153
166
  ],
154
- "description": "Readiness check for the container.",
167
+ "description": "Extra check the start waits for once the container is up (the image's healthcheck, else its first port accepting TCP), before setup, tasks and services run. Write `started` or one of `tcp`, `http`, `log` (matched against the container's logs) or `cmd` (run on the host). If it does not pass within its timeout (default 60s), the start fails.",
155
168
  "examples": [
156
169
  "started",
157
170
  {
@@ -162,10 +175,11 @@
162
175
  {
163
176
  "tcp": true
164
177
  }
165
- ]
178
+ ],
179
+ "markdownDescription": "Extra check the start waits for once the container is up (the image's healthcheck, else its first port accepting TCP), before setup, tasks and services run. Write `started` or one of `tcp`, `http`, `log` (matched against the container's logs) or `cmd` (run on the host). If it does not pass within its timeout (default 60s), the start fails."
166
180
  },
167
181
  "volumes": {
168
- "description": "Docker source:destination[:mode] mounts; relative sources use the owning worktree.",
182
+ "description": "Mounts as `source:destination[:mode]`. `./relative` and `/absolute` sources are bind mounts, relative ones from the owning worktree; a bare name is a named volume kept per project or per lane.",
169
183
  "examples": [
170
184
  [
171
185
  "./infra/cerbos:/policies:ro"
@@ -173,11 +187,13 @@
173
187
  ],
174
188
  "items": {
175
189
  "$ref": "#/$defs/Templated_string",
176
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
190
+ "description": "Mounts as `source:destination[:mode]`. `./relative` and `/absolute` sources are bind mounts, relative ones from the owning worktree; a bare name is a named volume kept per project or per lane.",
177
191
  "examples": [
178
- "${{ lane.index }}"
179
- ]
192
+ "./infra/cerbos:/policies:ro"
193
+ ],
194
+ "markdownDescription": "Mounts as `source:destination[:mode]`. `./relative` and `/absolute` sources are bind mounts, relative ones from the owning worktree; a bare name is a named volume kept per project or per lane."
180
195
  },
196
+ "markdownDescription": "Mounts as `source:destination[:mode]`. `./relative` and `/absolute` sources are bind mounts, relative ones from the owning worktree; a bare name is a named volume kept per project or per lane.",
181
197
  "type": [
182
198
  "array",
183
199
  "null"
@@ -188,27 +204,32 @@
188
204
  },
189
205
  "FnoxConfig": {
190
206
  "additionalProperties": false,
191
- "description": "Secrets profile selection for the project or a single unit.",
207
+ "description": "Which fnox profile secrets come from.",
192
208
  "examples": [
193
209
  {
194
210
  "profile": "dev"
195
211
  }
196
212
  ],
213
+ "markdownDescription": "Which fnox profile secrets come from.",
197
214
  "properties": {
198
215
  "profile": {
199
216
  "anyOf": [
200
217
  {
201
- "$ref": "#/$defs/Templated_string"
218
+ "$ref": "#/$defs/Templated_string",
219
+ "description": "",
220
+ "examples": [],
221
+ "markdownDescription": ""
202
222
  },
203
223
  {
204
224
  "type": "null"
205
225
  }
206
226
  ],
207
- "description": "Secrets profile passed to fnox; uses fnox's default profile when omitted.",
227
+ "description": "fnox profile whose secrets are loaded; fnox's default profile when omitted. Locked secrets are unlocked with `devlanes secrets unlock`.",
208
228
  "examples": [
209
229
  "dev",
210
230
  "admin-api"
211
- ]
231
+ ],
232
+ "markdownDescription": "fnox profile whose secrets are loaded; fnox's default profile when omitted. Locked secrets are unlocked with `devlanes secrets unlock`."
212
233
  }
213
234
  },
214
235
  "type": "object"
@@ -217,59 +238,51 @@
217
238
  "anyOf": [
218
239
  {
219
240
  "$ref": "#/$defs/Templated_string",
220
- "description": "Hostname routed to the service's primary port. Dot-separated labels of letters,\ndigits and inner hyphens, with an optional leading `*.` wildcard; case and a\ntrailing dot are ignored.",
221
- "examples": [
222
- "api.${{ lane.domain }}"
223
- ]
241
+ "description": "",
242
+ "examples": [],
243
+ "markdownDescription": ""
224
244
  },
225
245
  {
226
246
  "$ref": "#/$defs/HostRoute",
227
- "description": "Hostname with optional named port, fallback, and Host-header policy.",
228
- "examples": [
229
- {
230
- "fallback": true,
231
- "host": "*.${{ lane.domain }}"
232
- }
233
- ]
247
+ "description": "",
248
+ "examples": [],
249
+ "markdownDescription": ""
234
250
  }
235
251
  ],
236
- "description": "Hostname routed by the HTTP proxy, optionally with route settings.",
252
+ "description": "HTTPS hostname the devlanes proxy routes to this unit in the current lane: a plain hostname, or a mapping with `host` plus `port`, `fallback` or `host_header`. Keep names under `lane.domain` so lanes never claim the same name.",
237
253
  "examples": [
238
254
  "api.${{ lane.domain }}",
239
255
  {
240
256
  "fallback": true,
241
257
  "host": "*.${{ lane.domain }}"
242
258
  }
243
- ]
259
+ ],
260
+ "markdownDescription": "HTTPS hostname the devlanes proxy routes to this unit in the current lane: a plain hostname, or a mapping with `host` plus `port`, `fallback` or `host_header`. Keep names under `lane.domain` so lanes never claim the same name."
244
261
  },
245
262
  "HostHeader": {
246
- "description": "Host header sent to the upstream HTTP server.",
263
+ "description": "Host header sent to the unit behind a hostname.",
264
+ "enum": [
265
+ "preserve",
266
+ "upstream"
267
+ ],
268
+ "enumDescriptions": [
269
+ "Send the hostname the client requested, such as `api.acme-2.localhost`.",
270
+ "Send the local address and port the proxy connects to, such as `127.0.0.1:<port>`."
271
+ ],
247
272
  "examples": [
248
273
  "preserve",
249
274
  "upstream"
250
275
  ],
251
- "oneOf": [
252
- {
253
- "const": "preserve",
254
- "description": "Keep the Host header supplied by the client.",
255
- "examples": [
256
- "preserve"
257
- ],
258
- "type": "string"
259
- },
260
- {
261
- "const": "upstream",
262
- "description": "Send the upstream address and port as the Host header.",
263
- "examples": [
264
- "upstream"
265
- ],
266
- "type": "string"
267
- }
268
- ]
276
+ "markdownDescription": "Host header sent to the unit behind a hostname.",
277
+ "markdownEnumDescriptions": [
278
+ "Send the hostname the client requested, such as `api.acme-2.localhost`.",
279
+ "Send the local address and port the proxy connects to, such as `127.0.0.1:<port>`."
280
+ ],
281
+ "type": "string"
269
282
  },
270
283
  "HostRoute": {
271
284
  "additionalProperties": false,
272
- "description": "HTTP proxy route to a service or container port.",
285
+ "description": "Hostname route with options: a named port, catch-all behaviour or a rewritten Host header.",
273
286
  "examples": [
274
287
  {
275
288
  "host": "inspect.${{ lane.domain }}",
@@ -280,50 +293,61 @@
280
293
  "host": "*.${{ lane.domain }}"
281
294
  }
282
295
  ],
296
+ "markdownDescription": "Hostname route with options: a named port, catch-all behaviour or a rewritten Host header.",
283
297
  "properties": {
284
298
  "fallback": {
285
299
  "anyOf": [
286
300
  {
287
- "$ref": "#/$defs/Templated_boolean"
301
+ "$ref": "#/$defs/Templated_boolean",
302
+ "description": "",
303
+ "examples": [],
304
+ "markdownDescription": ""
288
305
  },
289
306
  {
290
307
  "type": "null"
291
308
  }
292
309
  ],
293
- "description": "Use this route only when no other hostname matches; defaults to false.",
310
+ "description": "Make this route a catch-all: it is used only when no other route, exact or wildcard, matches the requested hostname. Typical for `*.${{ lane.domain }}` on a gateway that should answer every subdomain other services do not claim. Defaults to false.",
294
311
  "examples": [
295
312
  true,
296
313
  false
297
- ]
314
+ ],
315
+ "markdownDescription": "Make this route a catch-all: it is used only when no other route, exact or wildcard, matches the requested hostname. Typical for `*.${{ lane.domain }}` on a gateway that should answer every subdomain other services do not claim. Defaults to false."
298
316
  },
299
317
  "host": {
300
318
  "$ref": "#/$defs/Templated_string",
301
- "description": "Exact hostname or `*.` wildcard pattern, evaluated for the current lane. Labels\nhold letters, digits and inner hyphens; case and a trailing dot are ignored.",
319
+ "description": "Hostname to route, evaluated for each lane. A leading `*.` makes a wildcard that matches any depth below it but not the name itself; quote it in YAML. Labels hold letters, digits and inner hyphens; case and a trailing dot are ignored.",
302
320
  "examples": [
303
321
  "api.${{ lane.domain }}",
304
322
  "*.${{ lane.domain }}"
305
- ]
323
+ ],
324
+ "markdownDescription": "Hostname to route, evaluated for each lane. A leading `*.` makes a wildcard that matches any depth below it but not the name itself; quote it in YAML. Labels hold letters, digits and inner hyphens; case and a trailing dot are ignored."
306
325
  },
307
326
  "host_header": {
308
327
  "anyOf": [
309
328
  {
310
- "$ref": "#/$defs/HostHeader"
329
+ "$ref": "#/$defs/HostHeader",
330
+ "description": "",
331
+ "examples": [],
332
+ "markdownDescription": ""
311
333
  },
312
334
  {
313
335
  "type": "null"
314
336
  }
315
337
  ],
316
- "description": "Preserve the browser's Host header by default, or rewrite it for the upstream.",
338
+ "description": "Host header the unit receives. Defaults to `preserve`, the hostname the browser asked for; `upstream` sends the local address, such as `127.0.0.1:<port>`, for servers that reject or misroute requests for a Host that is not their own address.",
317
339
  "examples": [
318
340
  "preserve",
319
341
  "upstream"
320
- ]
342
+ ],
343
+ "markdownDescription": "Host header the unit receives. Defaults to `preserve`, the hostname the browser asked for; `upstream` sends the local address, such as `127.0.0.1:<port>`, for servers that reject or misroute requests for a Host that is not their own address."
321
344
  },
322
345
  "port": {
323
- "description": "Named port to route to; uses the unit's primary port when omitted.",
346
+ "description": "Send this hostname to one of the unit's named ports instead of its main port. For a service it is a port name as in `self.ports.<name>`, and naming it here allocates that port; in `infra.hosts` it is the container port for Compose services, or the port's name for plain containers.",
324
347
  "examples": [
325
348
  "inspector"
326
349
  ],
350
+ "markdownDescription": "Send this hostname to one of the unit's named ports instead of its main port. For a service it is a port name as in `self.ports.<name>`, and naming it here allocates that port; in `infra.hosts` it is the container port for Compose services, or the port's name for plain containers.",
327
351
  "type": [
328
352
  "string",
329
353
  "null"
@@ -335,43 +359,71 @@
335
359
  ],
336
360
  "type": "object"
337
361
  },
362
+ "Hosts": {
363
+ "anyOf": [
364
+ {
365
+ "$ref": "#/$defs/HostConfig",
366
+ "description": "",
367
+ "examples": [],
368
+ "markdownDescription": ""
369
+ },
370
+ {
371
+ "items": {
372
+ "$ref": "#/$defs/HostConfig",
373
+ "description": "HTTPS hostname the devlanes proxy routes to this unit in the current lane: a plain hostname, or a mapping with `host` plus `port`, `fallback` or `host_header`. Keep names under `lane.domain` so lanes never claim the same name.",
374
+ "examples": [
375
+ "api.${{ lane.domain }}",
376
+ {
377
+ "fallback": true,
378
+ "host": "*.${{ lane.domain }}"
379
+ }
380
+ ],
381
+ "markdownDescription": "HTTPS hostname the devlanes proxy routes to this unit in the current lane: a plain hostname, or a mapping with `host` plus `port`, `fallback` or `host_header`. Keep names under `lane.domain` so lanes never claim the same name."
382
+ },
383
+ "type": "array"
384
+ }
385
+ ],
386
+ "description": "HTTPS hostnames the devlanes proxy routes to this unit, so a browser reaches it at `https://<host>`: one hostname or route mapping, or a list of them.",
387
+ "examples": [
388
+ "api.${{ lane.domain }}",
389
+ [
390
+ "api.${{ lane.domain }}",
391
+ {
392
+ "host": "inspect.${{ lane.domain }}",
393
+ "port": "inspector"
394
+ }
395
+ ]
396
+ ],
397
+ "markdownDescription": "HTTPS hostnames the devlanes proxy routes to this unit, so a browser reaches it at `https://<host>`: one hostname or route mapping, or a list of them."
398
+ },
338
399
  "HostsFile": {
339
- "description": "Whether project hostnames are also written to the system hosts file.",
400
+ "description": "Whether routed hostnames are also written to `/etc/hosts`.",
401
+ "enum": [
402
+ "auto",
403
+ "never",
404
+ "always"
405
+ ],
406
+ "enumDescriptions": [
407
+ "Write the exact hostnames only when the daemon's startup check finds that the system resolver does not answer `*.localhost` with a loopback address.",
408
+ "Never write hostnames to `/etc/hosts`. Set in the user config, it applies to every project whatever the project sets.",
409
+ "Always write the exact hostnames, even when the system resolver handles `*.localhost`, for programs that send `*.localhost` to DNS themselves."
410
+ ],
340
411
  "examples": [
341
412
  "auto",
342
413
  "never",
343
414
  "always"
344
415
  ],
345
- "oneOf": [
346
- {
347
- "const": "auto",
348
- "description": "Write entries when the system cannot resolve the configured hostnames.",
349
- "examples": [
350
- "auto"
351
- ],
352
- "type": "string"
353
- },
354
- {
355
- "const": "never",
356
- "description": "Do not write hosts entries.",
357
- "examples": [
358
- "never"
359
- ],
360
- "type": "string"
361
- },
362
- {
363
- "const": "always",
364
- "description": "Always write exact hostnames, including for runtimes without localhost subdomain support.",
365
- "examples": [
366
- "always"
367
- ],
368
- "type": "string"
369
- }
370
- ]
416
+ "markdownDescription": "Whether routed hostnames are also written to `/etc/hosts`.",
417
+ "markdownEnumDescriptions": [
418
+ "Write the exact hostnames only when the daemon's startup check finds that the system resolver does not answer `*.localhost` with a loopback address.",
419
+ "Never write hostnames to `/etc/hosts`. Set in the user config, it applies to every project whatever the project sets.",
420
+ "Always write the exact hostnames, even when the system resolver handles `*.localhost`, for programs that send `*.localhost` to DNS themselves."
421
+ ],
422
+ "type": "string"
371
423
  },
372
424
  "HttpReady": {
373
425
  "additionalProperties": false,
374
- "description": "HTTP readiness probe against the unit's local server.",
426
+ "description": "HTTP readiness probe against the unit's main port. Passes on a 2xx answer when `status` is unset, or on exactly `status` when it is set. Redirects are not followed.",
375
427
  "examples": [
376
428
  {
377
429
  "path": "/healthz",
@@ -379,50 +431,63 @@
379
431
  "timeout": "60s"
380
432
  }
381
433
  ],
434
+ "markdownDescription": "HTTP readiness probe against the unit's main port. Passes on a 2xx answer when `status` is unset, or on exactly `status` when it is set. Redirects are not followed.",
382
435
  "properties": {
383
436
  "path": {
384
437
  "anyOf": [
385
438
  {
386
- "$ref": "#/$defs/Templated_string"
439
+ "$ref": "#/$defs/Templated_string",
440
+ "description": "",
441
+ "examples": [],
442
+ "markdownDescription": ""
387
443
  },
388
444
  {
389
445
  "type": "null"
390
446
  }
391
447
  ],
392
- "description": "Request path, such as /healthz.",
448
+ "description": "Path requested with GET, such as `/healthz`; defaults to `/`.",
393
449
  "examples": [
394
450
  "/healthz",
395
451
  "/-/health"
396
- ]
452
+ ],
453
+ "markdownDescription": "Path requested with GET, such as `/healthz`; defaults to `/`."
397
454
  },
398
455
  "status": {
399
456
  "anyOf": [
400
457
  {
401
- "$ref": "#/$defs/Templated_uint16"
458
+ "$ref": "#/$defs/Templated_uint16",
459
+ "description": "",
460
+ "examples": [],
461
+ "markdownDescription": ""
402
462
  },
403
463
  {
404
464
  "type": "null"
405
465
  }
406
466
  ],
407
- "description": "HTTP response status required for readiness, between 100 and 599.",
467
+ "description": "Status code that counts as ready, between 100 and 599; only that exact code passes. When unset, any status from 200 to 299 counts, so a 404 or a redirect keeps waiting (redirects are not followed). Set it to accept another code, such as `302` for a route that redirects to a login.",
408
468
  "examples": [
409
469
  200
410
- ]
470
+ ],
471
+ "markdownDescription": "Status code that counts as ready, between 100 and 599; only that exact code passes. When unset, any status from 200 to 299 counts, so a 404 or a redirect keeps waiting (redirects are not followed). Set it to accept another code, such as `302` for a route that redirects to a login."
411
472
  },
412
473
  "timeout": {
413
474
  "anyOf": [
414
475
  {
415
- "$ref": "#/$defs/Templated_ConfigDuration"
476
+ "$ref": "#/$defs/Templated_ConfigDuration",
477
+ "description": "",
478
+ "examples": [],
479
+ "markdownDescription": ""
416
480
  },
417
481
  {
418
482
  "type": "null"
419
483
  }
420
484
  ],
421
- "description": "Maximum time to wait for the HTTP probe to succeed.",
485
+ "description": "How long the HTTP check may take to pass, replacing the check's `timeout`; defaults to 60s. When it runs out, a service is marked failed but keeps running, and for an infra container the start fails.",
422
486
  "examples": [
423
487
  "15s",
424
488
  "10m"
425
- ]
489
+ ],
490
+ "markdownDescription": "How long the HTTP check may take to pass, replacing the check's `timeout`; defaults to 60s. When it runs out, a service is marked failed but keeps running, and for an infra container the start fails."
426
491
  }
427
492
  },
428
493
  "type": "object"
@@ -431,23 +496,18 @@
431
496
  "anyOf": [
432
497
  {
433
498
  "$ref": "#/$defs/InfraCommandOptions",
434
- "description": "Command or script with lifecycle and failure-handling settings.",
435
- "examples": [
436
- {
437
- "ignore_errors": true,
438
- "run": "docker compose exec -T rabbitmq rabbitmqctl add_vhost ${{ lane.vhost }}"
439
- }
440
- ]
499
+ "description": "",
500
+ "examples": [],
501
+ "markdownDescription": ""
441
502
  },
442
503
  {
443
504
  "$ref": "#/$defs/Run",
444
- "description": "Shell command or argv without extra options.",
445
- "examples": [
446
- "docker compose exec -T postgres createdb acme_${{ lane.name }}"
447
- ]
505
+ "description": "",
506
+ "examples": [],
507
+ "markdownDescription": ""
448
508
  }
449
509
  ],
450
- "description": "Named infrastructure command in shorthand or object form.",
510
+ "description": "Command that prepares (`setup`) or removes (`teardown`) a lane's data: a command string or list, or a mapping with `run` and options such as `container`, `ignore_errors` and `timeout`. Task `steps` can run it as `infra.setup.<name>` or `infra.teardown.<name>`.",
451
511
  "examples": [
452
512
  "docker compose exec -T postgres createdb acme_${{ lane.name }}",
453
513
  {
@@ -459,11 +519,12 @@
459
519
  "s3://${{ lane.bucket }}"
460
520
  ]
461
521
  }
462
- ]
522
+ ],
523
+ "markdownDescription": "Command that prepares (`setup`) or removes (`teardown`) a lane's data: a command string or list, or a mapping with `run` and options such as `container`, `ignore_errors` and `timeout`. Task `steps` can run it as `infra.setup.<name>` or `infra.teardown.<name>`."
463
524
  },
464
525
  "InfraCommandOptions": {
465
526
  "additionalProperties": false,
466
- "description": "Setup or teardown command with optional container restart tracking.",
527
+ "description": "Setup or teardown command with options.",
467
528
  "examples": [
468
529
  {
469
530
  "container": "rabbitmq",
@@ -471,12 +532,14 @@
471
532
  "run": "docker compose exec -T rabbitmq rabbitmqctl add_vhost ${{ lane.vhost }}"
472
533
  }
473
534
  ],
535
+ "markdownDescription": "Setup or teardown command with options.",
474
536
  "properties": {
475
537
  "container": {
476
- "description": "Container whose restart invalidates this setup entry's completion record, so it runs again.",
538
+ "description": "Run this setup entry again whenever the named container has restarted since it last ran, for data that lives in the container and may be gone, such as a RabbitMQ vhost.",
477
539
  "examples": [
478
540
  "rabbitmq"
479
541
  ],
542
+ "markdownDescription": "Run this setup entry again whenever the named container has restarted since it last ran, for data that lives in the container and may be gone, such as a RabbitMQ vhost.",
480
543
  "type": [
481
544
  "string",
482
545
  "null"
@@ -485,59 +548,72 @@
485
548
  "cwd": {
486
549
  "anyOf": [
487
550
  {
488
- "$ref": "#/$defs/Templated_string"
551
+ "$ref": "#/$defs/Templated_string",
552
+ "description": "",
553
+ "examples": [],
554
+ "markdownDescription": ""
489
555
  },
490
556
  {
491
557
  "type": "null"
492
558
  }
493
559
  ],
494
- "description": "Working directory relative to the lane's worktree.",
560
+ "description": "Working directory, relative to the lane's worktree; defaults to the worktree root.",
495
561
  "examples": [
496
562
  "infra"
497
- ]
563
+ ],
564
+ "markdownDescription": "Working directory, relative to the lane's worktree; defaults to the worktree root."
498
565
  },
499
566
  "env": {
500
567
  "additionalProperties": {
501
568
  "$ref": "#/$defs/Templated_Scalar",
502
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
569
+ "description": "Variables for this command only.",
503
570
  "examples": [
504
- "${{ lane.index }}"
505
- ]
571
+ "us-east-1"
572
+ ],
573
+ "markdownDescription": "Variables for this command only."
506
574
  },
507
575
  "default": {},
508
- "description": "Environment overrides for this command.",
576
+ "description": "Variables for this command only.",
509
577
  "examples": [
510
578
  {
511
579
  "AWS_DEFAULT_REGION": "us-east-1"
512
580
  }
513
581
  ],
582
+ "markdownDescription": "Variables for this command only.",
514
583
  "type": "object"
515
584
  },
516
585
  "ignore_errors": {
517
586
  "anyOf": [
518
587
  {
519
- "$ref": "#/$defs/Templated_boolean"
588
+ "$ref": "#/$defs/Templated_boolean",
589
+ "description": "",
590
+ "examples": [],
591
+ "markdownDescription": ""
520
592
  },
521
593
  {
522
594
  "type": "null"
523
595
  }
524
596
  ],
525
- "description": "Continue when the command fails; defaults to false.",
597
+ "description": "Count a failed or timed-out command as done instead of failing the start, for commands whose \"already exists\" error is fine. Defaults to false.",
526
598
  "examples": [
527
599
  true,
528
600
  false
529
- ]
601
+ ],
602
+ "markdownDescription": "Count a failed or timed-out command as done instead of failing the start, for commands whose \"already exists\" error is fine. Defaults to false."
530
603
  },
531
604
  "run": {
532
605
  "anyOf": [
533
606
  {
534
- "$ref": "#/$defs/Run"
607
+ "$ref": "#/$defs/Run",
608
+ "description": "",
609
+ "examples": [],
610
+ "markdownDescription": ""
535
611
  },
536
612
  {
537
613
  "type": "null"
538
614
  }
539
615
  ],
540
- "description": "Shell text or argv; mutually exclusive with run_script.",
616
+ "description": "Command to run: a string runs through `/bin/sh -c`, a list runs the program directly. Write the command you mean, such as `docker compose exec -T postgres ...`; devlanes runs it as written. Required.",
541
617
  "examples": [
542
618
  "pnpm dev",
543
619
  [
@@ -546,47 +622,34 @@
546
622
  "--port",
547
623
  "${{ self.port }}"
548
624
  ]
549
- ]
550
- },
551
- "run_script": {
552
- "anyOf": [
553
- {
554
- "$ref": "#/$defs/RunScript"
555
- },
556
- {
557
- "type": "null"
558
- }
559
625
  ],
560
- "description": "Script source and name; mutually exclusive with run.",
561
- "examples": [
562
- "apps/api/package.json#dev",
563
- {
564
- "name": "fixtures",
565
- "source": "justfile"
566
- }
567
- ]
626
+ "markdownDescription": "Command to run: a string runs through `/bin/sh -c`, a list runs the program directly. Write the command you mean, such as `docker compose exec -T postgres ...`; devlanes runs it as written. Required."
568
627
  },
569
628
  "timeout": {
570
629
  "anyOf": [
571
630
  {
572
- "$ref": "#/$defs/Templated_ConfigDuration"
631
+ "$ref": "#/$defs/Templated_ConfigDuration",
632
+ "description": "",
633
+ "examples": [],
634
+ "markdownDescription": ""
573
635
  },
574
636
  {
575
637
  "type": "null"
576
638
  }
577
639
  ],
578
- "description": "Maximum run duration for this command.",
640
+ "description": "Longest the command may run. When it runs out, the command is stopped (SIGTERM, then SIGKILL after 10 seconds) and counts as failed, so a setup entry fails the start unless `ignore_errors` is set. No limit by default.",
579
641
  "examples": [
580
642
  "15s",
581
643
  "10m"
582
- ]
644
+ ],
645
+ "markdownDescription": "Longest the command may run. When it runs out, the command is stopped (SIGTERM, then SIGKILL after 10 seconds) and counts as failed, so a setup entry fails the start unless `ignore_errors` is set. No limit by default."
583
646
  }
584
647
  },
585
648
  "type": "object"
586
649
  },
587
650
  "InfraConfig": {
588
651
  "additionalProperties": false,
589
- "description": "Containers and per-lane setup/teardown commands started before application services.",
652
+ "description": "Containers the lanes depend on, started and waited on before setup, tasks and services, plus the commands that create and remove each lane's data in them. Processes reach a container through its host port, `${{ infra.<container>.port }}`.",
590
653
  "examples": [
591
654
  {
592
655
  "compose": "docker-compose.yml",
@@ -595,29 +658,34 @@
595
658
  ]
596
659
  }
597
660
  ],
661
+ "markdownDescription": "Containers the lanes depend on, started and waited on before setup, tasks and services, plus the commands that create and remove each lane's data in them. Processes reach a container through its host port, `${{ infra.<container>.port }}`.",
598
662
  "properties": {
599
663
  "compose": {
600
664
  "anyOf": [
601
665
  {
602
- "$ref": "#/$defs/ComposeFiles"
666
+ "$ref": "#/$defs/ComposeFiles",
667
+ "description": "",
668
+ "examples": [],
669
+ "markdownDescription": ""
603
670
  },
604
671
  {
605
672
  "type": "null"
606
673
  }
607
674
  ],
608
- "description": "Compose file or ordered list of Compose files.",
675
+ "description": "Compose file, or a list of files merged like repeated `-f`, relative to the project. Its services are shared by every lane unless listed in `per_lane`; the shared project runs from the primary worktree with that worktree's `.env` as `--env-file`.",
609
676
  "examples": [
610
677
  "docker-compose.yml",
611
678
  [
612
679
  "docker-compose.yml",
613
680
  "docker-compose.dev.yml"
614
681
  ]
615
- ]
682
+ ],
683
+ "markdownDescription": "Compose file, or a list of files merged like repeated `-f`, relative to the project. Its services are shared by every lane unless listed in `per_lane`; the shared project runs from the primary worktree with that worktree's `.env` as `--env-file`."
616
684
  },
617
685
  "containers": {
618
686
  "additionalProperties": {
619
687
  "$ref": "#/$defs/ContainerConfig",
620
- "description": "Plain Docker container managed alongside Compose services.",
688
+ "description": "Docker container run without a Compose file, shared by every lane or one per lane. Processes reach it through `${{ infra.<name>.ports.<port> }}`.",
621
689
  "examples": [
622
690
  {
623
691
  "image": "ghcr.io/cerbos/cerbos:0.40.0",
@@ -627,10 +695,11 @@
627
695
  "http": 3592
628
696
  }
629
697
  }
630
- ]
698
+ ],
699
+ "markdownDescription": "Docker container run without a Compose file, shared by every lane or one per lane. Processes reach it through `${{ infra.<name>.ports.<port> }}`."
631
700
  },
632
701
  "default": {},
633
- "description": "Plain Docker containers managed alongside Compose services.",
702
+ "description": "Docker containers run without a Compose file, keyed by name and reached as `infra.<name>` in expressions.",
634
703
  "examples": [
635
704
  {
636
705
  "cerbos": {
@@ -642,58 +711,65 @@
642
711
  }
643
712
  }
644
713
  ],
714
+ "markdownDescription": "Docker containers run without a Compose file, keyed by name and reached as `infra.<name>` in expressions.",
645
715
  "type": "object"
646
716
  },
647
717
  "env": {
648
718
  "additionalProperties": {
649
719
  "$ref": "#/$defs/Templated_Scalar",
650
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
720
+ "description": "Variables for `${VAR}` interpolation in the Compose files only; processes do not see them (use `global_env` for those).",
651
721
  "examples": [
652
- "${{ lane.index }}"
653
- ]
722
+ 15432
723
+ ],
724
+ "markdownDescription": "Variables for `${VAR}` interpolation in the Compose files only; processes do not see them (use `global_env` for those)."
654
725
  },
655
726
  "default": {},
656
- "description": "Variables for Compose interpolation, separate from process global_env.",
727
+ "description": "Variables for `${VAR}` interpolation in the Compose files only; processes do not see them (use `global_env` for those).",
657
728
  "examples": [
658
729
  {
659
730
  "ACME_DB_PORT": 15432
660
731
  }
661
732
  ],
733
+ "markdownDescription": "Variables for `${VAR}` interpolation in the Compose files only; processes do not see them (use `global_env` for those).",
662
734
  "type": "object"
663
735
  },
664
736
  "exclude": {
665
- "description": "Compose services never started by devlanes.",
737
+ "description": "Compose services devlanes never starts, such as a local reverse proxy the devlanes proxy replaces.",
666
738
  "examples": [
667
739
  [
668
740
  "caddy"
669
741
  ]
670
742
  ],
671
743
  "items": {
672
- "description": "Compose services never started by devlanes.",
744
+ "description": "Compose services devlanes never starts, such as a local reverse proxy the devlanes proxy replaces.",
673
745
  "examples": [
674
746
  "caddy"
675
747
  ],
748
+ "markdownDescription": "Compose services devlanes never starts, such as a local reverse proxy the devlanes proxy replaces.",
676
749
  "type": "string"
677
750
  },
751
+ "markdownDescription": "Compose services devlanes never starts, such as a local reverse proxy the devlanes proxy replaces.",
678
752
  "type": [
679
753
  "array",
680
754
  "null"
681
755
  ]
682
756
  },
683
757
  "hidden": {
684
- "description": "Containers whose ports must not be mirrored to remote clients.",
758
+ "description": "Containers whose ports are not mirrored to machines connected with `devlanes remote connect`.",
685
759
  "examples": [
686
760
  [
687
761
  "postgres"
688
762
  ]
689
763
  ],
690
764
  "items": {
691
- "description": "Containers whose ports must not be mirrored to remote clients.",
765
+ "description": "Containers whose ports are not mirrored to machines connected with `devlanes remote connect`.",
692
766
  "examples": [
693
767
  "postgres"
694
768
  ],
769
+ "markdownDescription": "Containers whose ports are not mirrored to machines connected with `devlanes remote connect`.",
695
770
  "type": "string"
696
771
  },
772
+ "markdownDescription": "Containers whose ports are not mirrored to machines connected with `devlanes remote connect`.",
697
773
  "type": [
698
774
  "array",
699
775
  "null"
@@ -701,69 +777,67 @@
701
777
  },
702
778
  "hosts": {
703
779
  "additionalProperties": {
704
- "description": "Container name to HTTP proxy route declarations.",
780
+ "$ref": "#/$defs/Hosts",
781
+ "description": "HTTPS hostnames the devlanes proxy routes to this unit, so a browser reaches it at `https://<host>`: one hostname or route mapping, or a list of them.",
705
782
  "examples": [
783
+ "api.${{ lane.domain }}",
706
784
  [
707
- "s3.${{ lane.domain }}"
708
- ]
709
- ],
710
- "items": {
711
- "$ref": "#/$defs/HostConfig",
712
- "description": "Hostname routed by the HTTP proxy, optionally with route settings.",
713
- "examples": [
714
785
  "api.${{ lane.domain }}",
715
786
  {
716
- "fallback": true,
717
- "host": "*.${{ lane.domain }}"
787
+ "host": "inspect.${{ lane.domain }}",
788
+ "port": "inspector"
718
789
  }
719
790
  ]
720
- },
721
- "type": "array"
791
+ ],
792
+ "markdownDescription": "HTTPS hostnames the devlanes proxy routes to this unit, so a browser reaches it at `https://<host>`: one hostname or route mapping, or a list of them."
722
793
  },
723
794
  "default": {},
724
- "description": "Container name to HTTP proxy route declarations.",
795
+ "description": "HTTPS hostnames for containers, keyed by Compose service or plain container name, so a browser can open a container's web port: a MinIO console or presigned S3 URLs, Mailpit, a mock OIDC provider. A hostname routes to the container's first published port unless its route sets `port`; each key takes one hostname or route, or a list.",
725
796
  "examples": [
726
797
  {
727
- "s3mock": [
728
- "s3.${{ lane.domain }}"
729
- ]
798
+ "s3mock": "s3.${{ lane.domain }}"
730
799
  }
731
800
  ],
801
+ "markdownDescription": "HTTPS hostnames for containers, keyed by Compose service or plain container name, so a browser can open a container's web port: a MinIO console or presigned S3 URLs, Mailpit, a mock OIDC provider. A hostname routes to the container's first published port unless its route sets `port`; each key takes one hostname or route, or a list.",
732
802
  "type": "object"
733
803
  },
734
804
  "per_lane": {
735
- "description": "Compose services instantiated separately in each lane.",
805
+ "description": "Compose services run once per lane instead of shared, each in its own Compose project with ports from the lane's block and its own named volumes, removed by `devlanes lane clean`. Needs Compose 2.24.4 or newer.",
736
806
  "examples": [
737
807
  [
738
808
  "mailpit"
739
809
  ]
740
810
  ],
741
811
  "items": {
742
- "description": "Compose services instantiated separately in each lane.",
812
+ "description": "Compose services run once per lane instead of shared, each in its own Compose project with ports from the lane's block and its own named volumes, removed by `devlanes lane clean`. Needs Compose 2.24.4 or newer.",
743
813
  "examples": [
744
814
  "mailpit"
745
815
  ],
816
+ "markdownDescription": "Compose services run once per lane instead of shared, each in its own Compose project with ports from the lane's block and its own named volumes, removed by `devlanes lane clean`. Needs Compose 2.24.4 or newer.",
746
817
  "type": "string"
747
818
  },
819
+ "markdownDescription": "Compose services run once per lane instead of shared, each in its own Compose project with ports from the lane's block and its own named volumes, removed by `devlanes lane clean`. Needs Compose 2.24.4 or newer.",
748
820
  "type": [
749
821
  "array",
750
822
  "null"
751
823
  ]
752
824
  },
753
825
  "profiles": {
754
- "description": "Compose profiles enabled when resolving and starting infrastructure.",
826
+ "description": "Compose profiles to enable when resolving and starting the Compose services.",
755
827
  "examples": [
756
828
  [
757
829
  "development"
758
830
  ]
759
831
  ],
760
832
  "items": {
761
- "description": "Compose profiles enabled when resolving and starting infrastructure.",
833
+ "description": "Compose profiles to enable when resolving and starting the Compose services.",
762
834
  "examples": [
763
835
  "development"
764
836
  ],
837
+ "markdownDescription": "Compose profiles to enable when resolving and starting the Compose services.",
765
838
  "type": "string"
766
839
  },
840
+ "markdownDescription": "Compose profiles to enable when resolving and starting the Compose services.",
767
841
  "type": [
768
842
  "array",
769
843
  "null"
@@ -772,21 +846,25 @@
772
846
  "project_name": {
773
847
  "anyOf": [
774
848
  {
775
- "$ref": "#/$defs/Templated_string"
849
+ "$ref": "#/$defs/Templated_string",
850
+ "description": "",
851
+ "examples": [],
852
+ "markdownDescription": ""
776
853
  },
777
854
  {
778
855
  "type": "null"
779
856
  }
780
857
  ],
781
- "description": "Shared Compose project name; defaults to Compose's own name.",
858
+ "description": "Name of the shared Compose project, fixed so `docker compose -p <name>` reaches the same containers outside devlanes. Defaults to the name Compose picks itself.",
782
859
  "examples": [
783
860
  "acme"
784
- ]
861
+ ],
862
+ "markdownDescription": "Name of the shared Compose project, fixed so `docker compose -p <name>` reaches the same containers outside devlanes. Defaults to the name Compose picks itself."
785
863
  },
786
864
  "setup": {
787
865
  "additionalProperties": {
788
866
  "$ref": "#/$defs/InfraCommand",
789
- "description": "Named infrastructure command in shorthand or object form.",
867
+ "description": "Command that prepares (`setup`) or removes (`teardown`) a lane's data: a command string or list, or a mapping with `run` and options such as `container`, `ignore_errors` and `timeout`. Task `steps` can run it as `infra.setup.<name>` or `infra.teardown.<name>`.",
790
868
  "examples": [
791
869
  "docker compose exec -T postgres createdb acme_${{ lane.name }}",
792
870
  {
@@ -798,21 +876,23 @@
798
876
  "s3://${{ lane.bucket }}"
799
877
  ]
800
878
  }
801
- ]
879
+ ],
880
+ "markdownDescription": "Command that prepares (`setup`) or removes (`teardown`) a lane's data: a command string or list, or a mapping with `run` and options such as `container`, `ignore_errors` and `timeout`. Task `steps` can run it as `infra.setup.<name>` or `infra.teardown.<name>`."
802
881
  },
803
882
  "default": {},
804
- "description": "Named commands run once per lane after containers become healthy.",
883
+ "description": "Named commands that prepare each lane's data, such as creating its database in a shared Postgres. They run once per lane after the containers are ready, all at the same time, and are recorded as done so later starts skip them; a failure stops the start unless the entry sets `ignore_errors`.",
805
884
  "examples": [
806
885
  {
807
886
  "database": "docker compose exec -T postgres createdb acme_${{ lane.name }}"
808
887
  }
809
888
  ],
889
+ "markdownDescription": "Named commands that prepare each lane's data, such as creating its database in a shared Postgres. They run once per lane after the containers are ready, all at the same time, and are recorded as done so later starts skip them; a failure stops the start unless the entry sets `ignore_errors`.",
810
890
  "type": "object"
811
891
  },
812
892
  "teardown": {
813
893
  "additionalProperties": {
814
894
  "$ref": "#/$defs/InfraCommand",
815
- "description": "Named infrastructure command in shorthand or object form.",
895
+ "description": "Command that prepares (`setup`) or removes (`teardown`) a lane's data: a command string or list, or a mapping with `run` and options such as `container`, `ignore_errors` and `timeout`. Task `steps` can run it as `infra.setup.<name>` or `infra.teardown.<name>`.",
816
896
  "examples": [
817
897
  "docker compose exec -T postgres createdb acme_${{ lane.name }}",
818
898
  {
@@ -824,15 +904,17 @@
824
904
  "s3://${{ lane.bucket }}"
825
905
  ]
826
906
  }
827
- ]
907
+ ],
908
+ "markdownDescription": "Command that prepares (`setup`) or removes (`teardown`) a lane's data: a command string or list, or a mapping with `run` and options such as `container`, `ignore_errors` and `timeout`. Task `steps` can run it as `infra.setup.<name>` or `infra.teardown.<name>`."
828
909
  },
829
910
  "default": {},
830
- "description": "Named cleanup commands run when a lane is explicitly cleaned.",
911
+ "description": "Named commands that remove a lane's data when the lane is cleaned (`devlanes lane clean`), one after another; never on stop. A failure is reported and the cleanup goes on.",
831
912
  "examples": [
832
913
  {
833
914
  "database": "docker compose exec -T postgres dropdb --if-exists acme_${{ lane.name }}"
834
915
  }
835
916
  ],
917
+ "markdownDescription": "Named commands that remove a lane's data when the lane is cleaned (`devlanes lane clean`), one after another; never on stop. A failure is reported and the cleanup goes on.",
836
918
  "type": "object"
837
919
  }
838
920
  },
@@ -840,7 +922,7 @@
840
922
  },
841
923
  "InjectedPresetOptions0": {
842
924
  "additionalProperties": false,
843
- "description": "Options under the service's `vite:` key.",
925
+ "description": "Options for `preset: vite`, which adds `--port <self.port> --strictPort --host 127.0.0.1` to the dev server command. `--strictPort` makes Vite exit when the port is taken instead of moving to another port the proxy does not know about.",
844
926
  "examples": [
845
927
  {
846
928
  "args": [
@@ -848,21 +930,24 @@
848
930
  ]
849
931
  }
850
932
  ],
933
+ "markdownDescription": "Options for `preset: vite`, which adds `--port <self.port> --strictPort --host 127.0.0.1` to the dev server command. `--strictPort` makes Vite exit when the port is taken instead of moving to another port the proxy does not know about.",
851
934
  "properties": {
852
935
  "args": {
853
- "description": "Extra arguments appended after the preset's flags.",
936
+ "description": "Extra arguments for the Vite command, added after the preset's port and host flags, for example `[--mode, staging]`. A local override or `extends` child that sets `args` replaces the inherited list. Default: none.",
854
937
  "examples": [
855
938
  [
856
939
  "--open"
857
940
  ]
858
941
  ],
859
942
  "items": {
860
- "description": "Extra arguments appended after the preset's flags.",
943
+ "description": "Extra arguments for the Vite command, added after the preset's port and host flags, for example `[--mode, staging]`. A local override or `extends` child that sets `args` replaces the inherited list. Default: none.",
861
944
  "examples": [
862
945
  "--open"
863
946
  ],
947
+ "markdownDescription": "Extra arguments for the Vite command, added after the preset's port and host flags, for example `[--mode, staging]`. A local override or `extends` child that sets `args` replaces the inherited list. Default: none.",
864
948
  "type": "string"
865
949
  },
950
+ "markdownDescription": "Extra arguments for the Vite command, added after the preset's port and host flags, for example `[--mode, staging]`. A local override or `extends` child that sets `args` replaces the inherited list. Default: none.",
866
951
  "type": [
867
952
  "array",
868
953
  "null"
@@ -873,29 +958,32 @@
873
958
  },
874
959
  "InjectedPresetOptions1": {
875
960
  "additionalProperties": false,
876
- "description": "Options under the service's `next:` key.",
961
+ "description": "Options for `preset: next`, which adds `--port <self.port> --hostname 127.0.0.1` to the `next dev` command. Without the hostname flag Next listens on `0.0.0.0` instead of loopback.",
877
962
  "examples": [
878
963
  {
879
964
  "args": [
880
- "--turbo"
965
+ "--turbopack"
881
966
  ]
882
967
  }
883
968
  ],
969
+ "markdownDescription": "Options for `preset: next`, which adds `--port <self.port> --hostname 127.0.0.1` to the `next dev` command. Without the hostname flag Next listens on `0.0.0.0` instead of loopback.",
884
970
  "properties": {
885
971
  "args": {
886
- "description": "Extra arguments appended after the preset's flags.",
972
+ "description": "Extra arguments for `next dev`, added after the preset's port and hostname flags, for example `[--turbopack]`. A local override or `extends` child that sets `args` replaces the inherited list. Default: none.",
887
973
  "examples": [
888
974
  [
889
- "--turbo"
975
+ "--turbopack"
890
976
  ]
891
977
  ],
892
978
  "items": {
893
- "description": "Extra arguments appended after the preset's flags.",
979
+ "description": "Extra arguments for `next dev`, added after the preset's port and hostname flags, for example `[--turbopack]`. A local override or `extends` child that sets `args` replaces the inherited list. Default: none.",
894
980
  "examples": [
895
- "--turbo"
981
+ "--turbopack"
896
982
  ],
983
+ "markdownDescription": "Extra arguments for `next dev`, added after the preset's port and hostname flags, for example `[--turbopack]`. A local override or `extends` child that sets `args` replaces the inherited list. Default: none.",
897
984
  "type": "string"
898
985
  },
986
+ "markdownDescription": "Extra arguments for `next dev`, added after the preset's port and hostname flags, for example `[--turbopack]`. A local override or `extends` child that sets `args` replaces the inherited list. Default: none.",
899
987
  "type": [
900
988
  "array",
901
989
  "null"
@@ -906,7 +994,7 @@
906
994
  },
907
995
  "InjectedPresetOptions2": {
908
996
  "additionalProperties": false,
909
- "description": "Options under the service's `wrangler:` key.",
997
+ "description": "Options for `preset: wrangler`, which gives each lane's `wrangler dev` its own `--port <self.port>`, `--inspector-port <self.ports.inspector>` and dev registry. The `inspector` port is allocated automatically. `args` that pass either port flag are refused by `devlanes config check`, and a `run_script` script that does is refused at start.",
910
998
  "examples": [
911
999
  {
912
1000
  "args": [
@@ -919,9 +1007,10 @@
919
1007
  }
920
1008
  }
921
1009
  ],
1010
+ "markdownDescription": "Options for `preset: wrangler`, which gives each lane's `wrangler dev` its own `--port <self.port>`, `--inspector-port <self.ports.inspector>` and dev registry. The `inspector` port is allocated automatically. `args` that pass either port flag are refused by `devlanes config check`, and a `run_script` script that does is refused at start.",
922
1011
  "properties": {
923
1012
  "args": {
924
- "description": "Extra arguments appended after the preset's flags.",
1013
+ "description": "Extra arguments for `wrangler dev`, added after the port and `--var` flags, for example `[--env-file, .dev.vars]`. `--port` and `--inspector-port` (or `--inspectorPort`) are refused here, since the preset already sets them per lane. A local override or `extends` child that sets `args` replaces the inherited list.",
925
1014
  "examples": [
926
1015
  [
927
1016
  "--env-file",
@@ -929,24 +1018,27 @@
929
1018
  ]
930
1019
  ],
931
1020
  "items": {
932
- "description": "Extra arguments appended after the preset's flags.",
1021
+ "description": "Extra arguments for `wrangler dev`, added after the port and `--var` flags, for example `[--env-file, .dev.vars]`. `--port` and `--inspector-port` (or `--inspectorPort`) are refused here, since the preset already sets them per lane. A local override or `extends` child that sets `args` replaces the inherited list.",
933
1022
  "examples": [
934
1023
  "--env-file",
935
1024
  ".dev.vars"
936
1025
  ],
1026
+ "markdownDescription": "Extra arguments for `wrangler dev`, added after the port and `--var` flags, for example `[--env-file, .dev.vars]`. `--port` and `--inspector-port` (or `--inspectorPort`) are refused here, since the preset already sets them per lane. A local override or `extends` child that sets `args` replaces the inherited list.",
937
1027
  "type": "string"
938
1028
  },
1029
+ "markdownDescription": "Extra arguments for `wrangler dev`, added after the port and `--var` flags, for example `[--env-file, .dev.vars]`. `--port` and `--inspector-port` (or `--inspectorPort`) are refused here, since the preset already sets them per lane. A local override or `extends` child that sets `args` replaces the inherited list.",
939
1030
  "type": [
940
1031
  "array",
941
1032
  "null"
942
1033
  ]
943
1034
  },
944
1035
  "isolate_registry": {
945
- "description": "Give each lane its own dev registry through `WRANGLER_REGISTRY_PATH`.\nDefault: true.",
1036
+ "description": "Points `WRANGLER_REGISTRY_PATH` at `wrangler-registry` in the lane's data folder (`${{ lane.data_path }}`), so service bindings resolve to workers of the same lane instead of same-named workers in another lane, also when two lanes run from one folder. Set `false` to drop the variable for this service, which then uses Wrangler's shared default registry. Default: `true`.",
946
1037
  "examples": [
947
1038
  true,
948
1039
  false
949
1040
  ],
1041
+ "markdownDescription": "Points `WRANGLER_REGISTRY_PATH` at `wrangler-registry` in the lane's data folder (`${{ lane.data_path }}`), so service bindings resolve to workers of the same lane instead of same-named workers in another lane, also when two lanes run from one folder. Set `false` to drop the variable for this service, which then uses Wrangler's shared default registry. Default: `true`.",
950
1042
  "type": [
951
1043
  "boolean",
952
1044
  "null"
@@ -954,22 +1046,24 @@
954
1046
  },
955
1047
  "vars": {
956
1048
  "additionalProperties": {
957
- "description": "Worker variables, passed as `--var KEY:VALUE`. Numbers and booleans are\naccepted because config scalars evaluate to them; wrangler sees strings.",
1049
+ "description": "Worker variables for this lane, each passed as `--var KEY:VALUE` so it overrides the same name in the Wrangler config's `vars`. Values can be strings, numbers or booleans (sent as text) and may use expressions such as `${{ self.public_url }}`; names must be non-empty and cannot contain `:`. Local overrides and `extends` children merge with inherited vars per name.",
958
1050
  "examples": [
959
1051
  "${{ self.public_url }}"
960
1052
  ],
1053
+ "markdownDescription": "Worker variables for this lane, each passed as `--var KEY:VALUE` so it overrides the same name in the Wrangler config's `vars`. Values can be strings, numbers or booleans (sent as text) and may use expressions such as `${{ self.public_url }}`; names must be non-empty and cannot contain `:`. Local overrides and `extends` children merge with inherited vars per name.",
961
1054
  "type": [
962
1055
  "string",
963
1056
  "number",
964
1057
  "boolean"
965
1058
  ]
966
1059
  },
967
- "description": "Worker variables, passed as `--var KEY:VALUE`. Numbers and booleans are\naccepted because config scalars evaluate to them; wrangler sees strings.",
1060
+ "description": "Worker variables for this lane, each passed as `--var KEY:VALUE` so it overrides the same name in the Wrangler config's `vars`. Values can be strings, numbers or booleans (sent as text) and may use expressions such as `${{ self.public_url }}`; names must be non-empty and cannot contain `:`. Local overrides and `extends` children merge with inherited vars per name.",
968
1061
  "examples": [
969
1062
  {
970
1063
  "PUBLIC_BASE_URL": "${{ self.public_url }}"
971
1064
  }
972
1065
  ],
1066
+ "markdownDescription": "Worker variables for this lane, each passed as `--var KEY:VALUE` so it overrides the same name in the Wrangler config's `vars`. Values can be strings, numbers or booleans (sent as text) and may use expressions such as `${{ self.public_url }}`; names must be non-empty and cannot contain `:`. Local overrides and `extends` children merge with inherited vars per name.",
973
1067
  "type": "object"
974
1068
  }
975
1069
  },
@@ -977,51 +1071,56 @@
977
1071
  },
978
1072
  "InjectedPresetOptions3": {
979
1073
  "additionalProperties": false,
980
- "description": "Options under the service's `tauri:` key, after expression evaluation.",
1074
+ "description": "Options for `preset: tauri`, which lets several Tauri v2 dev apps run side by side, one per lane and replica. The values go into a config overlay at `${{ lane.data_path }}/tauri/<service>.<replica>.conf.json`, passed with `--config` and in `DEVLANES_TAURI_CONFIG`, which Tauri merges over `tauri.conf.json`.",
981
1075
  "examples": [
982
1076
  {
983
1077
  "before_dev_command": "",
984
- "dev_url": "${{ svc.desktop_web.url }}",
1078
+ "dev_url": "${{ services.desktop_web.url }}",
985
1079
  "identifier": "io.acme.app.lane${{ lane.index }}.client${{ replica.index }}"
986
1080
  }
987
1081
  ],
1082
+ "markdownDescription": "Options for `preset: tauri`, which lets several Tauri v2 dev apps run side by side, one per lane and replica. The values go into a config overlay at `${{ lane.data_path }}/tauri/<service>.<replica>.conf.json`, passed with `--config` and in `DEVLANES_TAURI_CONFIG`, which Tauri merges over `tauri.conf.json`.",
988
1083
  "properties": {
989
1084
  "before_dev_command": {
990
- "description": "`build.beforeDevCommand` override. Set it to an empty string when the\nfrontend runs as its own devlanes service; otherwise every replica\nstarts its own frontend on the port from `tauri.conf.json`.",
1085
+ "description": "Replaces `build.beforeDevCommand`, the command Tauri runs before the app. Set it to `\"\"` when the frontend runs as its own devlanes service; otherwise every replica starts its own frontend on the port from `tauri.conf.json`. Default: the command in `tauri.conf.json`.",
991
1086
  "examples": [
992
1087
  "",
993
1088
  "pnpm dev"
994
1089
  ],
1090
+ "markdownDescription": "Replaces `build.beforeDevCommand`, the command Tauri runs before the app. Set it to `\"\"` when the frontend runs as its own devlanes service; otherwise every replica starts its own frontend on the port from `tauri.conf.json`. Default: the command in `tauri.conf.json`.",
995
1091
  "type": [
996
1092
  "string",
997
1093
  "null"
998
1094
  ]
999
1095
  },
1000
1096
  "dev_url": {
1001
- "description": "`build.devUrl`, usually another service's `url`.",
1097
+ "description": "Frontend address the app loads, written to `build.devUrl`. Usually another service's `url`, such as `${{ services.desktop_web.url }}`, so each lane's app loads that lane's frontend. Must start with `http://` or `https://`; when unset, `tauri.conf.json` decides.",
1002
1098
  "examples": [
1003
- "${{ svc.desktop_web.url }}"
1099
+ "${{ services.desktop_web.url }}"
1004
1100
  ],
1101
+ "markdownDescription": "Frontend address the app loads, written to `build.devUrl`. Usually another service's `url`, such as `${{ services.desktop_web.url }}`, so each lane's app loads that lane's frontend. Must start with `http://` or `https://`; when unset, `tauri.conf.json` decides.",
1005
1102
  "type": [
1006
1103
  "string",
1007
1104
  "null"
1008
1105
  ]
1009
1106
  },
1010
1107
  "identifier": {
1011
- "description": "Bundle identifier for this lane and replica. Required; it must differ per\nlane and replica, for example\n`com.acme.app.dev${{ '' if lane.index == 0 else '.lane' ~ lane.index }}.client${{ replica.index }}`.",
1108
+ "description": "Bundle identifier for this lane and replica, which keys the app data, webview data and keychain entries. Required and must differ per lane and replica, for example `com.acme.app.dev.lane${{ lane.index }}.client${{ replica.index }}`; two instances with the same value are refused at start. Only letters, digits, `-` and `.` are allowed, with no empty part between dots.",
1012
1109
  "examples": [
1013
1110
  "io.acme.app.lane${{ lane.index }}.client${{ replica.index }}"
1014
1111
  ],
1112
+ "markdownDescription": "Bundle identifier for this lane and replica, which keys the app data, webview data and keychain entries. Required and must differ per lane and replica, for example `com.acme.app.dev.lane${{ lane.index }}.client${{ replica.index }}`; two instances with the same value are refused at start. Only letters, digits, `-` and `.` are allowed, with no empty part between dots.",
1015
1113
  "type": [
1016
1114
  "string",
1017
1115
  "null"
1018
1116
  ]
1019
1117
  },
1020
1118
  "product_name": {
1021
- "description": "`productName` override, shown in the dock and window menus.",
1119
+ "description": "Replaces the app's `productName` from `tauri.conf.json` for this instance, for example to tell lanes apart. Default: the value in `tauri.conf.json`.",
1022
1120
  "examples": [
1023
1121
  "Acme Dev"
1024
1122
  ],
1123
+ "markdownDescription": "Replaces the app's `productName` from `tauri.conf.json` for this instance, for example to tell lanes apart. Default: the value in `tauri.conf.json`.",
1025
1124
  "type": [
1026
1125
  "string",
1027
1126
  "null"
@@ -1032,7 +1131,7 @@
1032
1131
  },
1033
1132
  "LanesConfig": {
1034
1133
  "additionalProperties": false,
1035
- "description": "Worktree registration policy and values computed for each lane.",
1134
+ "description": "Each Git worktree of the project is a lane with its own ports, hostnames and data. These settings decide which worktrees get a lane, where new ones are created, how many ports each lane reserves, and the values each lane computes.",
1036
1135
  "examples": [
1037
1136
  {
1038
1137
  "hosts_file": "always",
@@ -1042,39 +1141,48 @@
1042
1141
  }
1043
1142
  }
1044
1143
  ],
1144
+ "markdownDescription": "Each Git worktree of the project is a lane with its own ports, hostnames and data. These settings decide which worktrees get a lane, where new ones are created, how many ports each lane reserves, and the values each lane computes.",
1045
1145
  "properties": {
1046
1146
  "block_size": {
1047
1147
  "anyOf": [
1048
1148
  {
1049
- "$ref": "#/$defs/Templated_uint16"
1149
+ "$ref": "#/$defs/Templated_uint16",
1150
+ "description": "",
1151
+ "examples": [],
1152
+ "markdownDescription": ""
1050
1153
  },
1051
1154
  {
1052
1155
  "type": "null"
1053
1156
  }
1054
1157
  ],
1055
- "description": "Fixed port-block capacity; otherwise allocated with fifty percent headroom.",
1158
+ "description": "Number of ports reserved for each lane. By default devlanes counts the ports the config needs and adds half again, rounded up to ten. A fixed size smaller than what the config needs is an error.",
1056
1159
  "examples": [
1057
1160
  80
1058
- ]
1161
+ ],
1162
+ "markdownDescription": "Number of ports reserved for each lane. By default devlanes counts the ports the config needs and adds half again, rounded up to ten. A fixed size smaller than what the config needs is an error."
1059
1163
  },
1060
1164
  "hosts_file": {
1061
1165
  "anyOf": [
1062
1166
  {
1063
- "$ref": "#/$defs/HostsFile"
1167
+ "$ref": "#/$defs/HostsFile",
1168
+ "description": "",
1169
+ "examples": [],
1170
+ "markdownDescription": ""
1064
1171
  },
1065
1172
  {
1066
1173
  "type": "null"
1067
1174
  }
1068
1175
  ],
1069
- "description": "Project-specific host-file hint, overriding the user's policy.",
1176
+ "description": "Whether this project's hostnames are also written to `/etc/hosts`, for programs that cannot resolve `*.localhost` by themselves (Wrangler's workerd, musl or pure-Go binaries). Overrides the user's `hostnames.hosts_file` (default `auto`), except that a user `never` always wins. Writing needs the helper installed by `devlanes certs trust --hosts`, and only exact names are written, never wildcards.",
1070
1177
  "examples": [
1071
1178
  "auto",
1072
1179
  "never",
1073
1180
  "always"
1074
- ]
1181
+ ],
1182
+ "markdownDescription": "Whether this project's hostnames are also written to `/etc/hosts`, for programs that cannot resolve `*.localhost` by themselves (Wrangler's workerd, musl or pure-Go binaries). Overrides the user's `hostnames.hosts_file` (default `auto`), except that a user `never` always wins. Writing needs the helper installed by `devlanes certs trust --hosts`, and only exact names are written, never wildcards."
1075
1183
  },
1076
1184
  "matrix": {
1077
- "description": "Explicit lane values; entry N describes lane N. Mutually exclusive with vars.",
1185
+ "description": "Fixed values per lane instead of computed `vars`: entry N holds lane N's values, read as `lane.<name>`. A lane whose index has no entry fails to resolve. Cannot be combined with `vars`.",
1078
1186
  "examples": [
1079
1187
  [
1080
1188
  {
@@ -1090,12 +1198,13 @@
1090
1198
  "items": {
1091
1199
  "additionalProperties": {
1092
1200
  "$ref": "#/$defs/Templated_Scalar",
1093
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
1201
+ "description": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`.",
1094
1202
  "examples": [
1095
1203
  "${{ lane.index }}"
1096
- ]
1204
+ ],
1205
+ "markdownDescription": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`."
1097
1206
  },
1098
- "description": "Explicit lane values; entry N describes lane N. Mutually exclusive with vars.",
1207
+ "description": "Fixed values per lane instead of computed `vars`: entry N holds lane N's values, read as `lane.<name>`. A lane whose index has no entry fails to resolve. Cannot be combined with `vars`.",
1099
1208
  "examples": [
1100
1209
  {
1101
1210
  "domain": "acme.localhost",
@@ -1106,8 +1215,10 @@
1106
1215
  "port": 20100
1107
1216
  }
1108
1217
  ],
1218
+ "markdownDescription": "Fixed values per lane instead of computed `vars`: entry N holds lane N's values, read as `lane.<name>`. A lane whose index has no entry fails to resolve. Cannot be combined with `vars`.",
1109
1219
  "type": "object"
1110
1220
  },
1221
+ "markdownDescription": "Fixed values per lane instead of computed `vars`: entry N holds lane N's values, read as `lane.<name>`. A lane whose index has no entry fails to resolve. Cannot be combined with `vars`.",
1111
1222
  "type": [
1112
1223
  "array",
1113
1224
  "null"
@@ -1116,38 +1227,47 @@
1116
1227
  "max": {
1117
1228
  "anyOf": [
1118
1229
  {
1119
- "$ref": "#/$defs/Templated_uint32"
1230
+ "$ref": "#/$defs/Templated_uint32",
1231
+ "description": "",
1232
+ "examples": [],
1233
+ "markdownDescription": ""
1120
1234
  },
1121
1235
  {
1122
1236
  "type": "null"
1123
1237
  }
1124
1238
  ],
1125
- "description": "Maximum assigned lanes; existing lanes are retained if this is lowered.",
1239
+ "description": "Most lanes the project may hold at once; further worktrees stay unassigned, without ports. Lowering it keeps existing lanes. Unset, the number of lanes is limited only by free port blocks.",
1126
1240
  "examples": [
1127
1241
  6
1128
- ]
1242
+ ],
1243
+ "markdownDescription": "Most lanes the project may hold at once; further worktrees stay unassigned, without ports. Lowering it keeps existing lanes. Unset, the number of lanes is limited only by free port blocks."
1129
1244
  },
1130
1245
  "new_worktrees": {
1131
1246
  "anyOf": [
1132
1247
  {
1133
- "$ref": "#/$defs/NewWorktrees"
1248
+ "$ref": "#/$defs/NewWorktrees",
1249
+ "description": "",
1250
+ "examples": [],
1251
+ "markdownDescription": ""
1134
1252
  },
1135
1253
  {
1136
1254
  "type": "null"
1137
1255
  }
1138
1256
  ],
1139
- "description": "What happens when a worktree gets its lane; defaults to register. A worktree\nbecomes a lane the first time devlanes runs in it (or `lane new` creates it),\nnot because it exists; only the primary worktree gets one on its own.",
1257
+ "description": "What devlanes does when a worktree gets its lane: only reserve it, also set it up, or also start it. Defaults to `register`. A worktree gets a lane the first time devlanes runs in it or `devlanes lane new` creates it, not merely because it exists.",
1140
1258
  "examples": [
1141
1259
  "register",
1142
1260
  "setup",
1143
1261
  "start"
1144
- ]
1262
+ ],
1263
+ "markdownDescription": "What devlanes does when a worktree gets its lane: only reserve it, also set it up, or also start it. Defaults to `register`. A worktree gets a lane the first time devlanes runs in it or `devlanes lane new` creates it, not merely because it exists."
1145
1264
  },
1146
1265
  "primary": {
1147
- "description": "Branch assigned lane zero; defaults to the remote default branch, main, or master.",
1266
+ "description": "Branch whose worktree is lane 0, the main lane (`lanes.main` in expressions) and the only worktree that gets a lane by itself. Defaults to the remote's default branch, else `main` or `master`.",
1148
1267
  "examples": [
1149
1268
  "main"
1150
1269
  ],
1270
+ "markdownDescription": "Branch whose worktree is lane 0, the main lane (`lanes.main` in expressions) and the only worktree that gets a lane by itself. Defaults to the remote's default branch, else `main` or `master`.",
1151
1271
  "type": [
1152
1272
  "string",
1153
1273
  "null"
@@ -1156,120 +1276,109 @@
1156
1276
  "removed_worktrees": {
1157
1277
  "anyOf": [
1158
1278
  {
1159
- "$ref": "#/$defs/RemovedWorktrees"
1279
+ "$ref": "#/$defs/RemovedWorktrees",
1280
+ "description": "",
1281
+ "examples": [],
1282
+ "markdownDescription": ""
1160
1283
  },
1161
1284
  {
1162
1285
  "type": "null"
1163
1286
  }
1164
1287
  ],
1165
- "description": "What happens to a running lane whose worktree folder disappears; defaults to\nstop. A lane whose folder is gone and that runs nothing is cleaned; its logs\nstay until log retention removes them.",
1288
+ "description": "What happens to a running lane whose worktree folder is deleted. Defaults to `stop`. A lane whose folder is gone and that runs nothing is cleaned; its logs stay until log retention removes them.",
1166
1289
  "examples": [
1167
1290
  "stop",
1168
1291
  "clean"
1169
- ]
1292
+ ],
1293
+ "markdownDescription": "What happens to a running lane whose worktree folder is deleted. Defaults to `stop`. A lane whose folder is gone and that runs nothing is cleaned; its logs stay until log retention removes them."
1170
1294
  },
1171
1295
  "vars": {
1172
1296
  "additionalProperties": {
1173
1297
  "$ref": "#/$defs/Templated_Scalar",
1174
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
1298
+ "description": "Values computed for each lane and read as `${{ lane.<name> }}`, such as a database name, vhost, bucket or Redis index per lane. A var named `domain` replaces the default lane domain, and one named `port` fixes where the lane's port block starts. Cannot be combined with `matrix`.",
1175
1299
  "examples": [
1176
- "${{ lane.index }}"
1177
- ]
1300
+ "acme_${{ lane.name }}"
1301
+ ],
1302
+ "markdownDescription": "Values computed for each lane and read as `${{ lane.<name> }}`, such as a database name, vhost, bucket or Redis index per lane. A var named `domain` replaces the default lane domain, and one named `port` fixes where the lane's port block starts. Cannot be combined with `matrix`."
1178
1303
  },
1179
1304
  "default": {},
1180
- "description": "Scalar lane values available through the lane expression namespace.",
1305
+ "description": "Values computed for each lane and read as `${{ lane.<name> }}`, such as a database name, vhost, bucket or Redis index per lane. A var named `domain` replaces the default lane domain, and one named `port` fixes where the lane's port block starts. Cannot be combined with `matrix`.",
1181
1306
  "examples": [
1182
1307
  {
1183
1308
  "db_name": "acme_${{ lane.name }}"
1184
1309
  }
1185
1310
  ],
1311
+ "markdownDescription": "Values computed for each lane and read as `${{ lane.<name> }}`, such as a database name, vhost, bucket or Redis index per lane. A var named `domain` replaces the default lane domain, and one named `port` fixes where the lane's port block starts. Cannot be combined with `matrix`.",
1186
1312
  "type": "object"
1187
1313
  },
1188
1314
  "worktree_dir": {
1189
1315
  "anyOf": [
1190
1316
  {
1191
- "$ref": "#/$defs/Templated_string"
1317
+ "$ref": "#/$defs/Templated_string",
1318
+ "description": "",
1319
+ "examples": [],
1320
+ "markdownDescription": ""
1192
1321
  },
1193
1322
  {
1194
1323
  "type": "null"
1195
1324
  }
1196
1325
  ],
1197
- "description": "Directory for newly created worktrees, relative to the project.",
1326
+ "description": "Directory where `devlanes lane new` creates worktrees, relative to the project directory of the primary worktree. Defaults to `../<project>-lanes`. Expressions can only use `project.*` here.",
1198
1327
  "examples": [
1199
1328
  "../${{ project.name }}-lanes"
1200
- ]
1329
+ ],
1330
+ "markdownDescription": "Directory where `devlanes lane new` creates worktrees, relative to the project directory of the primary worktree. Defaults to `../<project>-lanes`. Expressions can only use `project.*` here."
1201
1331
  }
1202
1332
  },
1203
1333
  "type": "object"
1204
1334
  },
1205
1335
  "NewWorktrees": {
1206
- "description": "Action when a worktree gets its lane; defaults to register.",
1336
+ "description": "What devlanes does when a worktree gets its lane.",
1337
+ "enum": [
1338
+ "register",
1339
+ "setup",
1340
+ "start"
1341
+ ],
1342
+ "enumDescriptions": [
1343
+ "Only reserve the lane (its index, port block and domain); nothing runs until the lane is started.",
1344
+ "Also run infrastructure setup and `lane.create` tasks, leaving services stopped.",
1345
+ "Also start the lane with the default profile."
1346
+ ],
1207
1347
  "examples": [
1208
1348
  "register",
1209
1349
  "setup",
1210
1350
  "start"
1211
1351
  ],
1212
- "oneOf": [
1213
- {
1214
- "const": "register",
1215
- "description": "Only reserve the lane: its index, ports and domain.",
1216
- "examples": [
1217
- "register"
1218
- ],
1219
- "type": "string"
1220
- },
1221
- {
1222
- "const": "setup",
1223
- "description": "Also run infrastructure setup and lane.create tasks, leaving services stopped.",
1224
- "examples": [
1225
- "setup"
1226
- ],
1227
- "type": "string"
1228
- },
1229
- {
1230
- "const": "start",
1231
- "description": "Also start the lane with the default profile.",
1232
- "examples": [
1233
- "start"
1234
- ],
1235
- "type": "string"
1236
- }
1237
- ]
1352
+ "markdownDescription": "What devlanes does when a worktree gets its lane.",
1353
+ "markdownEnumDescriptions": [
1354
+ "Only reserve the lane (its index, port block and domain); nothing runs until the lane is started.",
1355
+ "Also run infrastructure setup and `lane.create` tasks, leaving services stopped.",
1356
+ "Also start the lane with the default profile."
1357
+ ],
1358
+ "type": "string"
1238
1359
  },
1239
1360
  "ProfileConfig": {
1240
1361
  "anyOf": [
1241
1362
  {
1242
- "description": "Service names, globs, or +tag selectors.",
1243
- "examples": [
1244
- [
1245
- "+api",
1246
- "+web"
1247
- ]
1248
- ],
1249
1363
  "items": {
1250
- "description": "Service names, globs, or +tag selectors.",
1364
+ "description": "Service names, globs and `+tag` selectors; short for `{ include: [...] }`.",
1251
1365
  "examples": [
1252
1366
  "+api",
1253
1367
  "+web"
1254
1368
  ],
1369
+ "markdownDescription": "Service names, globs and `+tag` selectors; short for `{ include: [...] }`.",
1255
1370
  "type": "string"
1256
1371
  },
1257
1372
  "type": "array"
1258
1373
  },
1259
1374
  {
1260
1375
  "$ref": "#/$defs/ProfileOptions",
1261
- "description": "Selection and overrides expressed as a mapping.",
1262
- "examples": [
1263
- {
1264
- "include": [
1265
- "+web"
1266
- ],
1267
- "infra": false
1268
- }
1269
- ]
1376
+ "description": "",
1377
+ "examples": [],
1378
+ "markdownDescription": ""
1270
1379
  }
1271
1380
  ],
1272
- "description": "Named service selection with optional environment, infrastructure, and script overrides.",
1381
+ "description": "Services started by `devlanes start --profile <name>` (`default` when no profile is given): a list of names, globs and `+tag` selectors, or a mapping that can also exclude services, skip infra, add `env` and remap script names. Several `--profile` flags start the union.",
1273
1382
  "examples": [
1274
1383
  [
1275
1384
  "+api",
@@ -1281,11 +1390,12 @@
1281
1390
  ],
1282
1391
  "infra": false
1283
1392
  }
1284
- ]
1393
+ ],
1394
+ "markdownDescription": "Services started by `devlanes start --profile <name>` (`default` when no profile is given): a list of names, globs and `+tag` selectors, or a mapping that can also exclude services, skip infra, add `env` and remap script names. Several `--profile` flags start the union."
1285
1395
  },
1286
1396
  "ProfileOptions": {
1287
1397
  "additionalProperties": false,
1288
- "description": "Service selectors and overrides applied when this profile is active.",
1398
+ "description": "Profile written as a mapping: which services it starts and what it changes for them.",
1289
1399
  "examples": [
1290
1400
  {
1291
1401
  "env": {
@@ -1300,26 +1410,29 @@
1300
1410
  }
1301
1411
  }
1302
1412
  ],
1413
+ "markdownDescription": "Profile written as a mapping: which services it starts and what it changes for them.",
1303
1414
  "properties": {
1304
1415
  "env": {
1305
1416
  "additionalProperties": {
1306
1417
  "$ref": "#/$defs/Templated_Scalar",
1307
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
1418
+ "description": "Variables for the services this profile starts, above their own `env`.",
1308
1419
  "examples": [
1309
- "${{ lane.index }}"
1310
- ]
1420
+ "production"
1421
+ ],
1422
+ "markdownDescription": "Variables for the services this profile starts, above their own `env`."
1311
1423
  },
1312
1424
  "default": {},
1313
- "description": "Profile environment overrides, applied above service environment values.",
1425
+ "description": "Variables for the services this profile starts, above their own `env`.",
1314
1426
  "examples": [
1315
1427
  {
1316
1428
  "NODE_ENV": "production"
1317
1429
  }
1318
1430
  ],
1431
+ "markdownDescription": "Variables for the services this profile starts, above their own `env`.",
1319
1432
  "type": "object"
1320
1433
  },
1321
1434
  "exclude": {
1322
- "description": "Selectors removed from the included service set.",
1435
+ "description": "Services removed from the `include` selection, by name, glob or `+tag`.",
1323
1436
  "examples": [
1324
1437
  [
1325
1438
  "+desktop",
@@ -1327,20 +1440,22 @@
1327
1440
  ]
1328
1441
  ],
1329
1442
  "items": {
1330
- "description": "Selectors removed from the included service set.",
1443
+ "description": "Services removed from the `include` selection, by name, glob or `+tag`.",
1331
1444
  "examples": [
1332
1445
  "+desktop",
1333
1446
  "sms-stub"
1334
1447
  ],
1448
+ "markdownDescription": "Services removed from the `include` selection, by name, glob or `+tag`.",
1335
1449
  "type": "string"
1336
1450
  },
1451
+ "markdownDescription": "Services removed from the `include` selection, by name, glob or `+tag`.",
1337
1452
  "type": [
1338
1453
  "array",
1339
1454
  "null"
1340
1455
  ]
1341
1456
  },
1342
1457
  "include": {
1343
- "description": "Service names, globs, and +tag selectors included by this profile.",
1458
+ "description": "Services this profile starts: names, globs such as `crm-*`, and `+tag` selectors. Globs skip services with `autostart: false`; names and tags include them.",
1344
1459
  "examples": [
1345
1460
  [
1346
1461
  "+api",
@@ -1351,14 +1466,16 @@
1351
1466
  ]
1352
1467
  ],
1353
1468
  "items": {
1354
- "description": "Service names, globs, and +tag selectors included by this profile.",
1469
+ "description": "Services this profile starts: names, globs such as `crm-*`, and `+tag` selectors. Globs skip services with `autostart: false`; names and tags include them.",
1355
1470
  "examples": [
1356
1471
  "+api",
1357
1472
  "+web",
1358
1473
  "*"
1359
1474
  ],
1475
+ "markdownDescription": "Services this profile starts: names, globs such as `crm-*`, and `+tag` selectors. Globs skip services with `autostart: false`; names and tags include them.",
1360
1476
  "type": "string"
1361
1477
  },
1478
+ "markdownDescription": "Services this profile starts: names, globs such as `crm-*`, and `+tag` selectors. Globs skip services with `autostart: false`; names and tags include them.",
1362
1479
  "type": [
1363
1480
  "array",
1364
1481
  "null"
@@ -1367,35 +1484,41 @@
1367
1484
  "infra": {
1368
1485
  "anyOf": [
1369
1486
  {
1370
- "$ref": "#/$defs/Templated_boolean"
1487
+ "$ref": "#/$defs/Templated_boolean",
1488
+ "description": "",
1489
+ "examples": [],
1490
+ "markdownDescription": ""
1371
1491
  },
1372
1492
  {
1373
1493
  "type": "null"
1374
1494
  }
1375
1495
  ],
1376
- "description": "Set false to skip containers and infra setup.",
1496
+ "description": "Set false to skip containers and `infra.setup`, for example to run only a frontend against another lane's backend. Services that reference `infra.*` then fail to resolve. With several profiles active, infra is skipped only when all of them set false. Defaults to true.",
1377
1497
  "examples": [
1378
1498
  false,
1379
1499
  true
1380
- ]
1500
+ ],
1501
+ "markdownDescription": "Set false to skip containers and `infra.setup`, for example to run only a frontend against another lane's backend. Services that reference `infra.*` then fail to resolve. With several profiles active, infra is skipped only when all of them set false. Defaults to true."
1381
1502
  },
1382
1503
  "run_scripts": {
1383
1504
  "additionalProperties": {
1384
- "description": "Remap service script names once, such as dev to start; source files stay unchanged.",
1505
+ "description": "Swap script names for services that use `run_script`: `{ dev: start }` runs the `start` script wherever a service's `run_script` names `dev`. The file stays the same, and each name is swapped once, not chained.",
1385
1506
  "examples": [
1386
1507
  "start"
1387
1508
  ],
1509
+ "markdownDescription": "Swap script names for services that use `run_script`: `{ dev: start }` runs the `start` script wherever a service's `run_script` names `dev`. The file stays the same, and each name is swapped once, not chained.",
1388
1510
  "minLength": 1,
1389
1511
  "pattern": "\\S",
1390
1512
  "type": "string"
1391
1513
  },
1392
1514
  "default": {},
1393
- "description": "Remap service script names once, such as dev to start; source files stay unchanged.",
1515
+ "description": "Swap script names for services that use `run_script`: `{ dev: start }` runs the `start` script wherever a service's `run_script` names `dev`. The file stays the same, and each name is swapped once, not chained.",
1394
1516
  "examples": [
1395
1517
  {
1396
1518
  "dev": "start"
1397
1519
  }
1398
1520
  ],
1521
+ "markdownDescription": "Swap script names for services that use `run_script`: `{ dev: start }` runs the `start` script wherever a service's `run_script` names `dev`. The file stays the same, and each name is swapped once, not chained.",
1399
1522
  "propertyNames": {
1400
1523
  "minLength": 1,
1401
1524
  "pattern": "\\S",
@@ -1408,7 +1531,7 @@
1408
1531
  },
1409
1532
  "ReadyCheck": {
1410
1533
  "additionalProperties": false,
1411
- "description": "Exactly one readiness mechanism is accepted by semantic validation.",
1534
+ "description": "Readiness probe: exactly one of `tcp`, `http`, `log` or `cmd`, retried until it passes or `timeout` runs out.",
1412
1535
  "examples": [
1413
1536
  {
1414
1537
  "http": {
@@ -1425,82 +1548,103 @@
1425
1548
  "cmd": "curl -fsS ${{ self.url }}/healthz"
1426
1549
  }
1427
1550
  ],
1551
+ "markdownDescription": "Readiness probe: exactly one of `tcp`, `http`, `log` or `cmd`, retried until it passes or `timeout` runs out.",
1428
1552
  "properties": {
1429
1553
  "cmd": {
1430
1554
  "anyOf": [
1431
1555
  {
1432
- "$ref": "#/$defs/Run"
1556
+ "$ref": "#/$defs/Run",
1557
+ "description": "",
1558
+ "examples": [],
1559
+ "markdownDescription": ""
1433
1560
  },
1434
1561
  {
1435
1562
  "type": "null"
1436
1563
  }
1437
1564
  ],
1438
- "description": "Retry this command until it exits successfully.",
1565
+ "description": "Ready once this command exits 0. It runs on the host through `/bin/sh` (a list is quoted into one command line) with its output discarded, is retried until the timeout, and each attempt is cut off after 10 seconds. For a service it gets the service's environment and working directory.",
1439
1566
  "examples": [
1440
1567
  "curl -fsS ${{ self.url }}/healthz"
1441
- ]
1568
+ ],
1569
+ "markdownDescription": "Ready once this command exits 0. It runs on the host through `/bin/sh` (a list is quoted into one command line) with its output discarded, is retried until the timeout, and each attempt is cut off after 10 seconds. For a service it gets the service's environment and working directory."
1442
1570
  },
1443
1571
  "http": {
1444
1572
  "anyOf": [
1445
1573
  {
1446
- "$ref": "#/$defs/HttpReady"
1574
+ "$ref": "#/$defs/HttpReady",
1575
+ "description": "",
1576
+ "examples": [],
1577
+ "markdownDescription": ""
1447
1578
  },
1448
1579
  {
1449
1580
  "type": "null"
1450
1581
  }
1451
1582
  ],
1452
- "description": "Wait for an HTTP response matching the requested path and status.",
1583
+ "description": "Ready once a GET request to `path` on the unit's main port answers with `status`, or with a 2xx status when `status` is unset. Redirects are not followed. Best when the app has a health route that fails while it is broken.",
1453
1584
  "examples": [
1454
1585
  {
1455
1586
  "path": "/healthz",
1456
1587
  "status": 200,
1457
1588
  "timeout": "60s"
1458
1589
  }
1459
- ]
1590
+ ],
1591
+ "markdownDescription": "Ready once a GET request to `path` on the unit's main port answers with `status`, or with a 2xx status when `status` is unset. Redirects are not followed. Best when the app has a health route that fails while it is broken."
1460
1592
  },
1461
1593
  "log": {
1462
1594
  "anyOf": [
1463
1595
  {
1464
- "$ref": "#/$defs/Templated_string"
1596
+ "$ref": "#/$defs/Templated_string",
1597
+ "description": "",
1598
+ "examples": [],
1599
+ "markdownDescription": ""
1465
1600
  },
1466
1601
  {
1467
1602
  "type": "null"
1468
1603
  }
1469
1604
  ],
1470
- "description": "Regular expression matched against output with terminal escape codes removed.",
1605
+ "description": "Ready once a line of output matches this regular expression, with colors and other terminal escape codes removed; recent lines printed before the check began count too. Suits workers without a port: have them print a line once connected.",
1471
1606
  "examples": [
1472
1607
  "Connected to Redis"
1473
- ]
1608
+ ],
1609
+ "markdownDescription": "Ready once a line of output matches this regular expression, with colors and other terminal escape codes removed; recent lines printed before the check began count too. Suits workers without a port: have them print a line once connected."
1474
1610
  },
1475
1611
  "tcp": {
1476
1612
  "anyOf": [
1477
1613
  {
1478
- "$ref": "#/$defs/TcpReady"
1614
+ "$ref": "#/$defs/TcpReady",
1615
+ "description": "",
1616
+ "examples": [],
1617
+ "markdownDescription": ""
1479
1618
  },
1480
1619
  {
1481
1620
  "type": "null"
1482
1621
  }
1483
1622
  ],
1484
- "description": "Wait for the primary port with true, or a named port such as inspector.",
1623
+ "description": "Ready once a port accepts a TCP connection on 127.0.0.1 or ::1, tried every 150 ms. `true` uses the unit's main port; a port name such as `inspector` uses that named port. It only proves the socket is open, not that the app works.",
1485
1624
  "examples": [
1486
1625
  true,
1487
1626
  "inspector"
1488
- ]
1627
+ ],
1628
+ "markdownDescription": "Ready once a port accepts a TCP connection on 127.0.0.1 or ::1, tried every 150 ms. `true` uses the unit's main port; a port name such as `inspector` uses that named port. It only proves the socket is open, not that the app works."
1489
1629
  },
1490
1630
  "timeout": {
1491
1631
  "anyOf": [
1492
1632
  {
1493
- "$ref": "#/$defs/Templated_ConfigDuration"
1633
+ "$ref": "#/$defs/Templated_ConfigDuration",
1634
+ "description": "",
1635
+ "examples": [],
1636
+ "markdownDescription": ""
1494
1637
  },
1495
1638
  {
1496
1639
  "type": "null"
1497
1640
  }
1498
1641
  ],
1499
- "description": "Maximum time to wait for the readiness check.",
1642
+ "description": "How long the check may take to pass; defaults to 60s. When it runs out, a service is marked failed (its process keeps running and is not restarted) and `after: services` tasks are skipped; for an infra container the start fails. Raise it for services that compile before they listen.",
1500
1643
  "examples": [
1501
1644
  "15s",
1502
1645
  "10m"
1503
- ]
1646
+ ],
1647
+ "markdownDescription": "How long the check may take to pass; defaults to 60s. When it runs out, a service is marked failed (its process keeps running and is not restarted) and `after: services` tasks are skipped; for an infra container the start fails. Raise it for services that compile before they listen."
1504
1648
  }
1505
1649
  },
1506
1650
  "type": "object"
@@ -1509,27 +1653,18 @@
1509
1653
  "anyOf": [
1510
1654
  {
1511
1655
  "$ref": "#/$defs/Started",
1512
- "description": "Treat a running process as ready without a probe.",
1513
- "examples": [
1514
- "started"
1515
- ]
1656
+ "description": "",
1657
+ "examples": [],
1658
+ "markdownDescription": ""
1516
1659
  },
1517
1660
  {
1518
1661
  "$ref": "#/$defs/ReadyCheck",
1519
- "description": "Wait for a TCP, HTTP, log, or command check to succeed.",
1520
- "examples": [
1521
- {
1522
- "http": {
1523
- "path": "/healthz"
1524
- }
1525
- },
1526
- {
1527
- "tcp": true
1528
- }
1529
- ]
1662
+ "description": "",
1663
+ "examples": [],
1664
+ "markdownDescription": ""
1530
1665
  }
1531
1666
  ],
1532
- "description": "Readiness policy; defaults to TCP for services with hosts and started for other services.",
1667
+ "description": "When a unit counts as ready. `devlanes start` waits for it, and `after: services` tasks run only once every started service is ready. Write `started`, or a mapping with one of `tcp`, `http`, `log` or `cmd` and an optional `timeout`.",
1533
1668
  "examples": [
1534
1669
  "started",
1535
1670
  {
@@ -1540,61 +1675,52 @@
1540
1675
  {
1541
1676
  "tcp": true
1542
1677
  }
1543
- ]
1678
+ ],
1679
+ "markdownDescription": "When a unit counts as ready. `devlanes start` waits for it, and `after: services` tasks run only once every started service is ready. Write `started`, or a mapping with one of `tcp`, `http`, `log` or `cmd` and an optional `timeout`."
1544
1680
  },
1545
1681
  "RemovedWorktrees": {
1546
- "description": "Action when a running lane's worktree folder disappears; defaults to stop.",
1682
+ "description": "What happens to a running lane whose worktree folder is deleted.",
1683
+ "enum": [
1684
+ "stop",
1685
+ "clean"
1686
+ ],
1687
+ "enumDescriptions": [
1688
+ "Stop the lane's processes; once nothing runs, the lane is cleaned.",
1689
+ "Clean the lane right away: stop its processes, run `lane.clean` tasks and `infra.teardown`, and free its index and ports."
1690
+ ],
1547
1691
  "examples": [
1548
1692
  "stop",
1549
1693
  "clean"
1550
1694
  ],
1551
- "oneOf": [
1552
- {
1553
- "const": "stop",
1554
- "description": "Stop the lane's processes; once nothing runs, the lane is cleaned.",
1555
- "examples": [
1556
- "stop"
1557
- ],
1558
- "type": "string"
1559
- },
1560
- {
1561
- "const": "clean",
1562
- "description": "Clean the lane right away: stop processes, run teardown, and free the lane.",
1563
- "examples": [
1564
- "clean"
1565
- ],
1566
- "type": "string"
1567
- }
1568
- ]
1695
+ "markdownDescription": "What happens to a running lane whose worktree folder is deleted.",
1696
+ "markdownEnumDescriptions": [
1697
+ "Stop the lane's processes; once nothing runs, the lane is cleaned.",
1698
+ "Clean the lane right away: stop its processes, run `lane.clean` tasks and `infra.teardown`, and free its index and ports."
1699
+ ],
1700
+ "type": "string"
1569
1701
  },
1570
1702
  "Run": {
1571
1703
  "anyOf": [
1572
1704
  {
1573
1705
  "$ref": "#/$defs/Templated_string",
1574
- "description": "Shell command text; supports pipelines and multiline scripts.",
1575
- "examples": [
1576
- "pnpm dev"
1577
- ]
1706
+ "description": "",
1707
+ "examples": [],
1708
+ "markdownDescription": ""
1578
1709
  },
1579
1710
  {
1580
- "description": "Executable and arguments passed directly without shell expansion.",
1581
- "examples": [
1582
- [
1583
- "node",
1584
- "server.mjs"
1585
- ]
1586
- ],
1587
1711
  "items": {
1588
1712
  "$ref": "#/$defs/Templated_string",
1589
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
1713
+ "description": "Program and arguments run directly, without shell expansion.",
1590
1714
  "examples": [
1591
- "${{ lane.index }}"
1592
- ]
1715
+ "node",
1716
+ "server.mjs"
1717
+ ],
1718
+ "markdownDescription": "Program and arguments run directly, without shell expansion."
1593
1719
  },
1594
1720
  "type": "array"
1595
1721
  }
1596
1722
  ],
1597
- "description": "Shell text runs in a shell; argv is passed directly to the executable.",
1723
+ "description": "Command as shell text, run through `/bin/sh -c`, or as a list of program and arguments run without a shell.",
1598
1724
  "examples": [
1599
1725
  "pnpm dev",
1600
1726
  [
@@ -1603,109 +1729,70 @@
1603
1729
  "--port",
1604
1730
  "${{ self.port }}"
1605
1731
  ]
1606
- ]
1732
+ ],
1733
+ "markdownDescription": "Command as shell text, run through `/bin/sh -c`, or as a list of program and arguments run without a shell."
1607
1734
  },
1608
1735
  "RunScript": {
1609
- "anyOf": [
1736
+ "additionalProperties": false,
1737
+ "description": "Script from a project file, as a mapping with `file` and `name`. devlanes picks the tool from the file name: package.json (the project's package manager), Makefile or `*.mk`, justfile or `*.just`, deno.json, pyproject.toml (uv or poetry) and Cargo.toml (a binary). In `devlanes.local.yaml` or a service that `extends` a template, either key can be left out to keep the inherited one.",
1738
+ "examples": [
1739
+ {
1740
+ "file": "apps/api/package.json",
1741
+ "name": "dev"
1742
+ },
1610
1743
  {
1611
- "description": "Source file followed by # and the script name.",
1744
+ "file": "justfile",
1745
+ "name": "fixtures"
1746
+ }
1747
+ ],
1748
+ "markdownDescription": "Script from a project file, as a mapping with `file` and `name`. devlanes picks the tool from the file name: package.json (the project's package manager), Makefile or `*.mk`, justfile or `*.just`, deno.json, pyproject.toml (uv or poetry) and Cargo.toml (a binary). In `devlanes.local.yaml` or a service that `extends` a template, either key can be left out to keep the inherited one.",
1749
+ "properties": {
1750
+ "file": {
1751
+ "description": "File that defines the script, relative to the worktree, such as `apps/api/package.json`. The script runs in this file's directory. Required unless an override or `extends` child keeps the inherited file.",
1612
1752
  "examples": [
1613
- "apps/api/package.json#dev"
1753
+ "apps/api/package.json",
1754
+ "Makefile",
1755
+ "justfile"
1614
1756
  ],
1757
+ "markdownDescription": "File that defines the script, relative to the worktree, such as `apps/api/package.json`. The script runs in this file's directory. Required unless an override or `extends` child keeps the inherited file.",
1615
1758
  "type": "string"
1616
1759
  },
1617
- {
1618
- "$ref": "#/$defs/ScriptSource",
1619
- "description": "Source file and script name written as separate fields.",
1760
+ "name": {
1761
+ "description": "Script, target, recipe, task or binary name in that file. Required unless an override or `extends` child keeps the inherited name.",
1620
1762
  "examples": [
1621
- {
1622
- "name": "fixtures",
1623
- "source": "justfile"
1624
- }
1625
- ]
1626
- }
1627
- ],
1628
- "description": "Script selected from a package manifest, Makefile, or justfile.",
1629
- "examples": [
1630
- "apps/api/package.json#dev",
1631
- {
1632
- "name": "fixtures",
1633
- "source": "justfile"
1763
+ "dev",
1764
+ "fixtures"
1765
+ ],
1766
+ "markdownDescription": "Script, target, recipe, task or binary name in that file. Required unless an override or `extends` child keeps the inherited name.",
1767
+ "type": "string"
1634
1768
  }
1635
- ]
1769
+ },
1770
+ "type": "object"
1636
1771
  },
1637
1772
  "Scalar": {
1638
1773
  "anyOf": [
1639
1774
  {
1640
- "description": "Text used verbatim in the environment or lane variables.",
1641
- "examples": [
1642
- "acme"
1643
- ],
1644
1775
  "type": "string"
1645
1776
  },
1646
1777
  {
1647
- "description": "Boolean value, rendered as true or false in process environments.",
1648
- "examples": [
1649
- true,
1650
- false
1651
- ],
1652
1778
  "type": "boolean"
1653
1779
  },
1654
1780
  {
1655
- "description": "Whole number, such as a port or lane index.",
1656
- "examples": [
1657
- 20000
1658
- ],
1659
1781
  "format": "int64",
1660
1782
  "type": "integer"
1661
1783
  },
1662
1784
  {
1663
- "description": "Decimal number, rendered as text in process environments.",
1664
- "examples": [
1665
- 1.5
1666
- ],
1667
1785
  "format": "double",
1668
1786
  "type": "number"
1669
1787
  }
1670
1788
  ],
1671
- "description": "A scalar suitable for environment variables and free-form lane variables.",
1789
+ "description": "A text, number or boolean value. Process environments receive it as text, such as `true` or `20000`.",
1672
1790
  "examples": [
1673
1791
  "acme",
1674
1792
  20000,
1675
1793
  true
1676
- ]
1677
- },
1678
- "ScriptSource": {
1679
- "additionalProperties": false,
1680
- "description": "A source file and the script or target to run from it.",
1681
- "examples": [
1682
- {
1683
- "name": "fixtures",
1684
- "source": "justfile"
1685
- }
1686
1794
  ],
1687
- "properties": {
1688
- "name": {
1689
- "default": "",
1690
- "description": "Script, target, or recipe name within the source file.\nRequired, unless an override or extends child keeps the inherited name.",
1691
- "examples": [
1692
- "dev",
1693
- "fixtures"
1694
- ],
1695
- "type": "string"
1696
- },
1697
- "source": {
1698
- "default": "",
1699
- "description": "Script source relative to the worktree, such as package.json, Makefile, or justfile.\nRequired, unless an override or extends child keeps the inherited source.",
1700
- "examples": [
1701
- "apps/api/package.json",
1702
- "Makefile",
1703
- "justfile"
1704
- ],
1705
- "type": "string"
1706
- }
1707
- },
1708
- "type": "object"
1795
+ "markdownDescription": "A text, number or boolean value. Process environments receive it as text, such as `true` or `20000`."
1709
1796
  },
1710
1797
  "ServiceConfig": {
1711
1798
  "additionalProperties": false,
@@ -1723,7 +1810,8 @@
1723
1810
  "description": "`vite` options require `preset: vite`.",
1724
1811
  "examples": [
1725
1812
  "vite"
1726
- ]
1813
+ ],
1814
+ "markdownDescription": "`vite` options require `preset: vite`."
1727
1815
  }
1728
1816
  }
1729
1817
  }
@@ -1741,7 +1829,8 @@
1741
1829
  "description": "`next` options require `preset: next`.",
1742
1830
  "examples": [
1743
1831
  "next"
1744
- ]
1832
+ ],
1833
+ "markdownDescription": "`next` options require `preset: next`."
1745
1834
  }
1746
1835
  }
1747
1836
  }
@@ -1759,7 +1848,8 @@
1759
1848
  "description": "`wrangler` options require `preset: wrangler`.",
1760
1849
  "examples": [
1761
1850
  "wrangler"
1762
- ]
1851
+ ],
1852
+ "markdownDescription": "`wrangler` options require `preset: wrangler`."
1763
1853
  }
1764
1854
  }
1765
1855
  }
@@ -1777,76 +1867,90 @@
1777
1867
  "description": "`tauri` options require `preset: tauri`.",
1778
1868
  "examples": [
1779
1869
  "tauri"
1780
- ]
1870
+ ],
1871
+ "markdownDescription": "`tauri` options require `preset: tauri`."
1781
1872
  }
1782
1873
  }
1783
1874
  }
1784
1875
  }
1785
1876
  ],
1786
- "description": "Service settings after typed local and template merging.",
1877
+ "description": "A long-running process started with the lane, such as a dev server or a worker. Each service gets its own port from the lane's block (`PORT`, `${{ self.port }}`); devlanes stops it with the lane and never restarts it when it exits. A service needs `run` or `run_script`, directly or through `extends`.",
1787
1878
  "examples": [
1788
1879
  {
1789
- "hosts": [
1790
- "api.${{ lane.domain }}"
1791
- ],
1792
- "run_script": "apps/api/package.json#dev",
1880
+ "hosts": "api.${{ lane.domain }}",
1881
+ "run_script": {
1882
+ "file": "apps/api/package.json",
1883
+ "name": "dev"
1884
+ },
1793
1885
  "tags": [
1794
1886
  "api"
1795
1887
  ]
1796
1888
  }
1797
1889
  ],
1890
+ "markdownDescription": "A long-running process started with the lane, such as a dev server or a worker. Each service gets its own port from the lane's block (`PORT`, `${{ self.port }}`); devlanes stops it with the lane and never restarts it when it exits. A service needs `run` or `run_script`, directly or through `extends`.",
1798
1891
  "properties": {
1799
1892
  "autostart": {
1800
1893
  "anyOf": [
1801
1894
  {
1802
- "$ref": "#/$defs/Templated_boolean"
1895
+ "$ref": "#/$defs/Templated_boolean",
1896
+ "description": "",
1897
+ "examples": [],
1898
+ "markdownDescription": ""
1803
1899
  },
1804
1900
  {
1805
1901
  "type": "null"
1806
1902
  }
1807
1903
  ],
1808
- "description": "Start by default unless explicitly selected; defaults to true.",
1904
+ "description": "Set false for an on-demand service: a plain `devlanes start` without a `default` profile skips it, and so do globs such as `*` in profiles. Naming it or its `+tag`, on the command line or in a profile, still starts it. An expression is evaluated for each lane and acts like the `true` or `false` it gives, such as `${{ lane.index == 0 }}` to autostart only in the main lane. With `replicas`, the service starts all its copies when any copy's expression gives true. Defaults to true.",
1809
1905
  "examples": [
1810
1906
  true,
1811
1907
  false
1812
- ]
1908
+ ],
1909
+ "markdownDescription": "Set false for an on-demand service: a plain `devlanes start` without a `default` profile skips it, and so do globs such as `*` in profiles. Naming it or its `+tag`, on the command line or in a profile, still starts it. An expression is evaluated for each lane and acts like the `true` or `false` it gives, such as `${{ lane.index == 0 }}` to autostart only in the main lane. With `replicas`, the service starts all its copies when any copy's expression gives true. Defaults to true."
1813
1910
  },
1814
1911
  "cwd": {
1815
1912
  "anyOf": [
1816
1913
  {
1817
- "$ref": "#/$defs/Templated_string"
1914
+ "$ref": "#/$defs/Templated_string",
1915
+ "description": "",
1916
+ "examples": [],
1917
+ "markdownDescription": ""
1818
1918
  },
1819
1919
  {
1820
1920
  "type": "null"
1821
1921
  }
1822
1922
  ],
1823
- "description": "Working directory relative to the lane worktree.",
1923
+ "description": "Directory `run` starts in, relative to the lane's worktree; defaults to the worktree root. Platform detection also looks here. Cannot be combined with `run_script`, which runs in the script file's directory; `devlanes config check` refuses the pair, including a `cwd` inherited through `extends`.",
1824
1924
  "examples": [
1825
1925
  "apps/api"
1826
- ]
1926
+ ],
1927
+ "markdownDescription": "Directory `run` starts in, relative to the lane's worktree; defaults to the worktree root. Platform detection also looks here. Cannot be combined with `run_script`, which runs in the script file's directory; `devlanes config check` refuses the pair, including a `cwd` inherited through `extends`."
1827
1928
  },
1828
1929
  "env": {
1829
1930
  "additionalProperties": {
1830
1931
  "$ref": "#/$defs/Templated_Scalar",
1831
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
1932
+ "description": "Environment variables for this service's process. They override `global_env` and are overridden by the active profile's `env` and `--env`; values may be text, numbers, booleans or `${{ ... }}` expressions. Setting `PORT` replaces the port devlanes allocated; a literal number is the same in every lane, so lanes collide.",
1832
1933
  "examples": [
1833
- "${{ lane.index }}"
1834
- ]
1934
+ "${{ services.api.url }}"
1935
+ ],
1936
+ "markdownDescription": "Environment variables for this service's process. They override `global_env` and are overridden by the active profile's `env` and `--env`; values may be text, numbers, booleans or `${{ ... }}` expressions. Setting `PORT` replaces the port devlanes allocated; a literal number is the same in every lane, so lanes collide."
1835
1937
  },
1836
1938
  "default": {},
1837
- "description": "Service-specific environment overrides, above global_env.",
1939
+ "description": "Environment variables for this service's process. They override `global_env` and are overridden by the active profile's `env` and `--env`; values may be text, numbers, booleans or `${{ ... }}` expressions. Setting `PORT` replaces the port devlanes allocated; a literal number is the same in every lane, so lanes collide.",
1838
1940
  "examples": [
1839
1941
  {
1840
- "API_URL": "${{ svc.api.url }}"
1942
+ "API_URL": "${{ services.api.url }}"
1841
1943
  }
1842
1944
  ],
1945
+ "markdownDescription": "Environment variables for this service's process. They override `global_env` and are overridden by the active profile's `env` and `--env`; values may be text, numbers, booleans or `${{ ... }}` expressions. Setting `PORT` replaces the port devlanes allocated; a literal number is the same in every lane, so lanes collide.",
1843
1946
  "type": "object"
1844
1947
  },
1845
1948
  "extends": {
1846
- "description": "Template name; resolved after devlanes.local.yaml is merged.",
1949
+ "description": "Template under `templates` whose settings this service inherits; its own settings apply on top. Mappings (`env`, `ready`, `stop`, `fnox`, preset options, `run_script`) merge by key, lists (`tags`, `hosts`, `sync`) and single values replace. Setting `run` or `run_script` drops the inherited other one, and one `ready` probe (`tcp`, `http`, `log`, `cmd`) drops the inherited probe. Templates may extend other templates and are resolved after devlanes.local.yaml is merged.",
1847
1950
  "examples": [
1848
1951
  "fastify-api"
1849
1952
  ],
1953
+ "markdownDescription": "Template under `templates` whose settings this service inherits; its own settings apply on top. Mappings (`env`, `ready`, `stop`, `fnox`, preset options, `run_script`) merge by key, lists (`tags`, `hosts`, `sync`) and single values replace. Setting `run` or `run_script` drops the inherited other one, and one `ready` probe (`tcp`, `http`, `log`, `cmd`) drops the inherited probe. Templates may extend other templates and are resolved after devlanes.local.yaml is merged.",
1850
1954
  "type": [
1851
1955
  "string",
1852
1956
  "null"
@@ -1855,37 +1959,57 @@
1855
1959
  "fnox": {
1856
1960
  "anyOf": [
1857
1961
  {
1858
- "$ref": "#/$defs/FnoxConfig"
1962
+ "$ref": "#/$defs/FnoxConfig",
1963
+ "description": "",
1964
+ "examples": [],
1965
+ "markdownDescription": ""
1859
1966
  },
1860
1967
  {
1861
1968
  "type": "null"
1862
1969
  }
1863
1970
  ],
1864
- "description": "Override the project fnox profile for this service.",
1971
+ "description": "Secrets profile for this service, replacing the project's `fnox.profile`. fnox puts that profile's secrets into the service's environment and `${{ env.X }}`; use it when one service needs different credentials.",
1865
1972
  "examples": [
1866
1973
  {
1867
1974
  "profile": "dev"
1868
1975
  }
1869
- ]
1976
+ ],
1977
+ "markdownDescription": "Secrets profile for this service, replacing the project's `fnox.profile`. fnox puts that profile's secrets into the service's environment and `${{ env.X }}`; use it when one service needs different credentials."
1870
1978
  },
1871
1979
  "hidden": {
1872
1980
  "anyOf": [
1873
1981
  {
1874
- "$ref": "#/$defs/Templated_boolean"
1982
+ "$ref": "#/$defs/Templated_boolean",
1983
+ "description": "",
1984
+ "examples": [],
1985
+ "markdownDescription": ""
1875
1986
  },
1876
1987
  {
1877
1988
  "type": "null"
1878
1989
  }
1879
1990
  ],
1880
- "description": "Prevent remote port mirroring; defaults to false.",
1991
+ "description": "Keep this service's ports and hosts off machines connected with `devlanes remote connect`: they are not mirrored there and cannot be reached from there. The service still runs and works on this machine. Defaults to false.",
1881
1992
  "examples": [
1882
1993
  false,
1883
1994
  true
1884
- ]
1995
+ ],
1996
+ "markdownDescription": "Keep this service's ports and hosts off machines connected with `devlanes remote connect`: they are not mirrored there and cannot be reached from there. The service still runs and works on this machine. Defaults to false."
1885
1997
  },
1886
1998
  "hosts": {
1887
- "description": "HTTP hosts, wildcard hosts, and routes to named ports.",
1999
+ "anyOf": [
2000
+ {
2001
+ "$ref": "#/$defs/Hosts",
2002
+ "description": "",
2003
+ "examples": [],
2004
+ "markdownDescription": ""
2005
+ },
2006
+ {
2007
+ "type": "null"
2008
+ }
2009
+ ],
2010
+ "description": "Hostnames the devlanes HTTPS proxy routes to this service, so `https://<host>` reaches it. One entry or a list; an entry is a hostname or a mapping with `host` and optional `port` (a named port), `fallback` and `host_header`. A `*.` wildcard matches subdomains, and an exact host wins over it. The first non-wildcard host becomes `public_url` and `DEVLANES_PUBLIC_URL`, non-wildcard hosts join the lane's `DEVLANES_ORIGINS`, and having hosts makes the default `ready` a TCP check. Keep them under `${{ lane.domain }}` so lanes do not collide.",
1888
2011
  "examples": [
2012
+ "api.${{ lane.domain }}",
1889
2013
  [
1890
2014
  "api.${{ lane.domain }}"
1891
2015
  ],
@@ -1896,28 +2020,15 @@
1896
2020
  }
1897
2021
  ]
1898
2022
  ],
1899
- "items": {
1900
- "$ref": "#/$defs/HostConfig",
1901
- "description": "Hostname routed by the HTTP proxy, optionally with route settings.",
1902
- "examples": [
1903
- "api.${{ lane.domain }}",
1904
- {
1905
- "fallback": true,
1906
- "host": "*.${{ lane.domain }}"
1907
- }
1908
- ]
1909
- },
1910
- "type": [
1911
- "array",
1912
- "null"
1913
- ]
2023
+ "markdownDescription": "Hostnames the devlanes HTTPS proxy routes to this service, so `https://<host>` reaches it. One entry or a list; an entry is a hostname or a mapping with `host` and optional `port` (a named port), `fallback` and `host_header`. A `*.` wildcard matches subdomains, and an exact host wins over it. The first non-wildcard host becomes `public_url` and `DEVLANES_PUBLIC_URL`, non-wildcard hosts join the lane's `DEVLANES_ORIGINS`, and having hosts makes the default `ready` a TCP check. Keep them under `${{ lane.domain }}` so lanes do not collide."
1914
2024
  },
1915
2025
  "name": {
1916
- "description": "Display name shown in the TUI and human output instead of the service key;\nany text, spaces allowed. Commands, references and `svc.<key>` keep using the key.",
2026
+ "description": "Label shown in the TUI and human output instead of the service key; any text, spaces allowed, with ` #N` added per replica. Commands, profiles and `${{ services.<key> }}` keep using the key.",
1917
2027
  "examples": [
1918
2028
  "Admin API",
1919
2029
  "Storefront (web)"
1920
2030
  ],
2031
+ "markdownDescription": "Label shown in the TUI and human output instead of the service key; any text, spaces allowed, with ` #N` added per replica. Commands, profiles and `${{ services.<key> }}` keep using the key.",
1921
2032
  "type": [
1922
2033
  "string",
1923
2034
  "null"
@@ -1925,18 +2036,18 @@
1925
2036
  },
1926
2037
  "next": {
1927
2038
  "$ref": "#/$defs/InjectedPresetOptions1",
1928
- "description": "Options under the service's `next:` key.",
2039
+ "description": "Options for `preset: next`, which adds `--port <self.port> --hostname 127.0.0.1` to the `next dev` command. Without the hostname flag Next listens on `0.0.0.0` instead of loopback.",
1929
2040
  "examples": [
1930
2041
  {
1931
2042
  "args": [
1932
- "--turbo"
2043
+ "--turbopack"
1933
2044
  ]
1934
2045
  }
1935
2046
  ],
1936
- "type": "object"
2047
+ "markdownDescription": "Options for `preset: next`, which adds `--port <self.port> --hostname 127.0.0.1` to the `next dev` command. Without the hostname flag Next listens on `0.0.0.0` instead of loopback."
1937
2048
  },
1938
2049
  "platform": {
1939
- "description": "Platform: node, bun, deno, python or rust; detected from project files when omitted.",
2050
+ "description": "Runtime of the service: `node`, `bun`, `deno`, `python` or `rust`. It decides which variables devlanes adds, such as the devlanes CA (`NODE_EXTRA_CA_CERTS`, `SSL_CERT_FILE`) and color settings. Detected from the `run_script` file or the working directory when omitted; set it only when detection is wrong.",
1940
2051
  "examples": [
1941
2052
  "node",
1942
2053
  "bun",
@@ -1944,13 +2055,14 @@
1944
2055
  "python",
1945
2056
  "rust"
1946
2057
  ],
2058
+ "markdownDescription": "Runtime of the service: `node`, `bun`, `deno`, `python` or `rust`. It decides which variables devlanes adds, such as the devlanes CA (`NODE_EXTRA_CA_CERTS`, `SSL_CERT_FILE`) and color settings. Detected from the `run_script` file or the working directory when omitted; set it only when detection is wrong.",
1947
2059
  "type": [
1948
2060
  "string",
1949
2061
  "null"
1950
2062
  ]
1951
2063
  },
1952
2064
  "preset": {
1953
- "description": "Registered framework preset; options use a sibling key with this name.",
2065
+ "description": "Framework integration that adds the flags and variables a dev server needs to run in a lane, such as `--port <port> --strictPort --host 127.0.0.1` for `vite`, so you do not pass them yourself. Requires `run_script`; with a hand-written `run`, drop the preset and pass its flags yourself. Its options go under a sibling key with the preset's name, for example `vite: { args: [--open] }`.",
1954
2066
  "enum": [
1955
2067
  "vite",
1956
2068
  "next",
@@ -1963,6 +2075,7 @@
1963
2075
  "wrangler",
1964
2076
  "tauri"
1965
2077
  ],
2078
+ "markdownDescription": "Framework integration that adds the flags and variables a dev server needs to run in a lane, such as `--port <port> --strictPort --host 127.0.0.1` for `vite`, so you do not pass them yourself. Requires `run_script`; with a hand-written `run`, drop the preset and pass its flags yourself. Its options go under a sibling key with the preset's name, for example `vite: { args: [--open] }`.",
1966
2079
  "type": [
1967
2080
  "string",
1968
2081
  "null"
@@ -1971,28 +2084,35 @@
1971
2084
  "pty": {
1972
2085
  "anyOf": [
1973
2086
  {
1974
- "$ref": "#/$defs/Templated_boolean"
2087
+ "$ref": "#/$defs/Templated_boolean",
2088
+ "description": "",
2089
+ "examples": [],
2090
+ "markdownDescription": ""
1975
2091
  },
1976
2092
  {
1977
2093
  "type": "null"
1978
2094
  }
1979
2095
  ],
1980
- "description": "Use a pseudoterminal; defaults to true.",
2096
+ "description": "Run the service in a pseudoterminal, so it sees a terminal as it would in your shell and you can type into it from the TUI. Set false to use pipes instead: stdin is empty and stdout and stderr are read separately. Defaults to true.",
1981
2097
  "examples": [
1982
2098
  true,
1983
2099
  false
1984
- ]
2100
+ ],
2101
+ "markdownDescription": "Run the service in a pseudoterminal, so it sees a terminal as it would in your shell and you can type into it from the TUI. Set false to use pipes instead: stdin is empty and stdout and stderr are read separately. Defaults to true."
1985
2102
  },
1986
2103
  "ready": {
1987
2104
  "anyOf": [
1988
2105
  {
1989
- "$ref": "#/$defs/ReadyConfig"
2106
+ "$ref": "#/$defs/ReadyConfig",
2107
+ "description": "",
2108
+ "examples": [],
2109
+ "markdownDescription": ""
1990
2110
  },
1991
2111
  {
1992
2112
  "type": "null"
1993
2113
  }
1994
2114
  ],
1995
- "description": "Readiness policy; defaults to TCP with hosts, otherwise started.",
2115
+ "description": "When the service counts as up: `devlanes start` waits for it, and `after: services` tasks run once every started service is ready. Write `started` (ready once the process runs) or a mapping with one of `tcp`, `http`, `log` or `cmd`, plus `timeout` (default 60s). Without it, a service with `hosts` waits for TCP on its port and one without hosts uses `started`. A check that times out marks the service failed, which also skips `after: services` tasks; the process keeps running and is not restarted.",
1996
2116
  "examples": [
1997
2117
  "started",
1998
2118
  {
@@ -2003,33 +2123,37 @@
2003
2123
  {
2004
2124
  "tcp": true
2005
2125
  }
2006
- ]
2126
+ ],
2127
+ "markdownDescription": "When the service counts as up: `devlanes start` waits for it, and `after: services` tasks run once every started service is ready. Write `started` (ready once the process runs) or a mapping with one of `tcp`, `http`, `log` or `cmd`, plus `timeout` (default 60s). Without it, a service with `hosts` waits for TCP on its port and one without hosts uses `started`. A check that times out marks the service failed, which also skips `after: services` tasks; the process keeps running and is not restarted."
2007
2128
  },
2008
2129
  "replicas": {
2009
- "anyOf": [
2010
- {
2011
- "$ref": "#/$defs/Templated_uint16"
2012
- },
2013
- {
2014
- "type": "null"
2015
- }
2016
- ],
2017
- "description": "Number of instances; replica.index starts at one. Defaults to one.",
2130
+ "description": "Number of copies of this service to run, each with its own port, for example several queue workers. They run as `<name>.1` to `<name>.N`, `${{ replica.index }}` counts from 1, and `services.<name>` in expressions means the first copy; use `${{ replica.index }}` in `hosts` to give each copy its own hostname. A plain number of at least 1, the same in every lane, because each copy reserves ports in every lane's block; `${{ ... }}` expressions are not allowed. Defaults to 1.",
2018
2131
  "examples": [
2019
2132
  1,
2020
2133
  2
2134
+ ],
2135
+ "format": "uint16",
2136
+ "markdownDescription": "Number of copies of this service to run, each with its own port, for example several queue workers. They run as `<name>.1` to `<name>.N`, `${{ replica.index }}` counts from 1, and `services.<name>` in expressions means the first copy; use `${{ replica.index }}` in `hosts` to give each copy its own hostname. A plain number of at least 1, the same in every lane, because each copy reserves ports in every lane's block; `${{ ... }}` expressions are not allowed. Defaults to 1.",
2137
+ "maximum": 65535,
2138
+ "minimum": 0,
2139
+ "type": [
2140
+ "integer",
2141
+ "null"
2021
2142
  ]
2022
2143
  },
2023
2144
  "run": {
2024
2145
  "anyOf": [
2025
2146
  {
2026
- "$ref": "#/$defs/Run"
2147
+ "$ref": "#/$defs/Run",
2148
+ "description": "",
2149
+ "examples": [],
2150
+ "markdownDescription": ""
2027
2151
  },
2028
2152
  {
2029
2153
  "type": "null"
2030
2154
  }
2031
2155
  ],
2032
- "description": "Shell command or argv; exactly one of run and run_script is required after inheritance.",
2156
+ "description": "Command that starts the service. A string runs with `/bin/sh -c`, so pipes, `&&` and multi-line scripts work; a list is an argv run directly without a shell. It starts in the lane's worktree root unless `cwd` is set. devlanes runs it exactly as written, so it cannot be combined with `preset`; prefer `run_script` when the command is a script in a project file. Exactly one of `run` and `run_script` is required after templates are applied.",
2033
2157
  "examples": [
2034
2158
  "pnpm dev",
2035
2159
  [
@@ -2038,45 +2162,57 @@
2038
2162
  "--port",
2039
2163
  "${{ self.port }}"
2040
2164
  ]
2041
- ]
2165
+ ],
2166
+ "markdownDescription": "Command that starts the service. A string runs with `/bin/sh -c`, so pipes, `&&` and multi-line scripts work; a list is an argv run directly without a shell. It starts in the lane's worktree root unless `cwd` is set. devlanes runs it exactly as written, so it cannot be combined with `preset`; prefer `run_script` when the command is a script in a project file. Exactly one of `run` and `run_script` is required after templates are applied."
2042
2167
  },
2043
2168
  "run_script": {
2044
2169
  "anyOf": [
2045
2170
  {
2046
- "$ref": "#/$defs/RunScript"
2171
+ "$ref": "#/$defs/RunScript",
2172
+ "description": "",
2173
+ "examples": [],
2174
+ "markdownDescription": ""
2047
2175
  },
2048
2176
  {
2049
2177
  "type": "null"
2050
2178
  }
2051
2179
  ],
2052
- "description": "Script file and name, in shorthand or object form.",
2180
+ "description": "Script to run from a project file, as `{ file, name }`, such as `{ file: apps/api/package.json, name: dev }`. devlanes picks the tool from the file name (`package.json`, `Makefile`, `justfile`, `deno.json`, `pyproject.toml`, `Cargo.toml`), runs the script in that file's directory and checks that it exists before starting. Presets need it to place their flags, and a profile's `run_scripts` can swap the script name. Exactly one of `run` and `run_script` is required after templates are applied.",
2053
2181
  "examples": [
2054
- "apps/api/package.json#dev",
2055
2182
  {
2056
- "name": "fixtures",
2057
- "source": "justfile"
2183
+ "file": "apps/api/package.json",
2184
+ "name": "dev"
2185
+ },
2186
+ {
2187
+ "file": "justfile",
2188
+ "name": "fixtures"
2058
2189
  }
2059
- ]
2190
+ ],
2191
+ "markdownDescription": "Script to run from a project file, as `{ file, name }`, such as `{ file: apps/api/package.json, name: dev }`. devlanes picks the tool from the file name (`package.json`, `Makefile`, `justfile`, `deno.json`, `pyproject.toml`, `Cargo.toml`), runs the script in that file's directory and checks that it exists before starting. Presets need it to place their flags, and a profile's `run_scripts` can swap the script name. Exactly one of `run` and `run_script` is required after templates are applied."
2060
2192
  },
2061
2193
  "stop": {
2062
2194
  "anyOf": [
2063
2195
  {
2064
- "$ref": "#/$defs/StopConfig"
2196
+ "$ref": "#/$defs/StopConfig",
2197
+ "description": "",
2198
+ "examples": [],
2199
+ "markdownDescription": ""
2065
2200
  },
2066
2201
  {
2067
2202
  "type": "null"
2068
2203
  }
2069
2204
  ],
2070
- "description": "Stop signal and grace period before killing the process tree.",
2205
+ "description": "How devlanes stops the service: `signal` goes to its whole process tree, and after `timeout` every process still running is SIGKILLed. The service then shows as stopped and is not restarted. Defaults to SIGTERM and 10s.",
2071
2206
  "examples": [
2072
2207
  {
2073
2208
  "signal": "SIGINT",
2074
2209
  "timeout": "15s"
2075
2210
  }
2076
- ]
2211
+ ],
2212
+ "markdownDescription": "How devlanes stops the service: `signal` goes to its whole process tree, and after `timeout` every process still running is SIGKILLed. The service then shows as stopped and is not restarted. Defaults to SIGTERM and 10s."
2077
2213
  },
2078
2214
  "sync": {
2079
- "description": "Folders copied to stable local or remote-client paths.",
2215
+ "description": "Folders mirrored while the service runs, such as build output a browser loads unpacked from a fixed path. Each entry copies `from` to `to` (default `~/devlanes/sync/<project>/<lane>/<folder>`) and follows changes; machines connected with `devlanes remote connect` get their own copy.",
2080
2216
  "examples": [
2081
2217
  [
2082
2218
  {
@@ -2089,7 +2225,7 @@
2089
2225
  ],
2090
2226
  "items": {
2091
2227
  "$ref": "#/$defs/SyncConfig",
2092
- "description": "Directory copied to a stable local or remote-client path.",
2228
+ "description": "Folder copied to a fixed path and kept up to date, for build output another program loads from a stable location, such as an unpacked browser extension. Also streams to machines connected with `devlanes remote connect`.",
2093
2229
  "examples": [
2094
2230
  {
2095
2231
  "from": "apps/extension/.output/chrome-mv3",
@@ -2097,15 +2233,17 @@
2097
2233
  "*.map"
2098
2234
  ]
2099
2235
  }
2100
- ]
2236
+ ],
2237
+ "markdownDescription": "Folder copied to a fixed path and kept up to date, for build output another program loads from a stable location, such as an unpacked browser extension. Also streams to machines connected with `devlanes remote connect`."
2101
2238
  },
2239
+ "markdownDescription": "Folders mirrored while the service runs, such as build output a browser loads unpacked from a fixed path. Each entry copies `from` to `to` (default `~/devlanes/sync/<project>/<lane>/<folder>`) and follows changes; machines connected with `devlanes remote connect` get their own copy.",
2102
2240
  "type": [
2103
2241
  "array",
2104
2242
  "null"
2105
2243
  ]
2106
2244
  },
2107
2245
  "tags": {
2108
- "description": "Tags used by +tag profile selectors.",
2246
+ "description": "Labels for selecting and grouping services. `+tag` selects every service with the tag in profiles, in commands such as `devlanes start +api`, and in the TUI filter. Tags also set the service's color: the first tag with a `color` under the top-level `tags` wins, then the first tag naming a category such as `api`, `web` or `worker`, so put that tag first.",
2109
2247
  "examples": [
2110
2248
  [
2111
2249
  "api",
@@ -2119,15 +2257,17 @@
2119
2257
  ]
2120
2258
  ],
2121
2259
  "items": {
2122
- "description": "Tags used by +tag profile selectors.",
2260
+ "description": "Labels for selecting and grouping services. `+tag` selects every service with the tag in profiles, in commands such as `devlanes start +api`, and in the TUI filter. Tags also set the service's color: the first tag with a `color` under the top-level `tags` wins, then the first tag naming a category such as `api`, `web` or `worker`, so put that tag first.",
2123
2261
  "examples": [
2124
2262
  "api",
2125
2263
  "essential",
2126
2264
  "worker",
2127
2265
  "web"
2128
2266
  ],
2267
+ "markdownDescription": "Labels for selecting and grouping services. `+tag` selects every service with the tag in profiles, in commands such as `devlanes start +api`, and in the TUI filter. Tags also set the service's color: the first tag with a `color` under the top-level `tags` wins, then the first tag naming a category such as `api`, `web` or `worker`, so put that tag first.",
2129
2268
  "type": "string"
2130
2269
  },
2270
+ "markdownDescription": "Labels for selecting and grouping services. `+tag` selects every service with the tag in profiles, in commands such as `devlanes start +api`, and in the TUI filter. Tags also set the service's color: the first tag with a `color` under the top-level `tags` wins, then the first tag naming a category such as `api`, `web` or `worker`, so put that tag first.",
2131
2271
  "type": [
2132
2272
  "array",
2133
2273
  "null"
@@ -2135,19 +2275,19 @@
2135
2275
  },
2136
2276
  "tauri": {
2137
2277
  "$ref": "#/$defs/InjectedPresetOptions3",
2138
- "description": "Options under the service's `tauri:` key, after expression evaluation.",
2278
+ "description": "Options for `preset: tauri`, which lets several Tauri v2 dev apps run side by side, one per lane and replica. The values go into a config overlay at `${{ lane.data_path }}/tauri/<service>.<replica>.conf.json`, passed with `--config` and in `DEVLANES_TAURI_CONFIG`, which Tauri merges over `tauri.conf.json`.",
2139
2279
  "examples": [
2140
2280
  {
2141
2281
  "before_dev_command": "",
2142
- "dev_url": "${{ svc.desktop_web.url }}",
2282
+ "dev_url": "${{ services.desktop_web.url }}",
2143
2283
  "identifier": "io.acme.app.lane${{ lane.index }}.client${{ replica.index }}"
2144
2284
  }
2145
2285
  ],
2146
- "type": "object"
2286
+ "markdownDescription": "Options for `preset: tauri`, which lets several Tauri v2 dev apps run side by side, one per lane and replica. The values go into a config overlay at `${{ lane.data_path }}/tauri/<service>.<replica>.conf.json`, passed with `--config` and in `DEVLANES_TAURI_CONFIG`, which Tauri merges over `tauri.conf.json`."
2147
2287
  },
2148
2288
  "vite": {
2149
2289
  "$ref": "#/$defs/InjectedPresetOptions0",
2150
- "description": "Options under the service's `vite:` key.",
2290
+ "description": "Options for `preset: vite`, which adds `--port <self.port> --strictPort --host 127.0.0.1` to the dev server command. `--strictPort` makes Vite exit when the port is taken instead of moving to another port the proxy does not know about.",
2151
2291
  "examples": [
2152
2292
  {
2153
2293
  "args": [
@@ -2155,11 +2295,11 @@
2155
2295
  ]
2156
2296
  }
2157
2297
  ],
2158
- "type": "object"
2298
+ "markdownDescription": "Options for `preset: vite`, which adds `--port <self.port> --strictPort --host 127.0.0.1` to the dev server command. `--strictPort` makes Vite exit when the port is taken instead of moving to another port the proxy does not know about."
2159
2299
  },
2160
2300
  "wrangler": {
2161
2301
  "$ref": "#/$defs/InjectedPresetOptions2",
2162
- "description": "Options under the service's `wrangler:` key.",
2302
+ "description": "Options for `preset: wrangler`, which gives each lane's `wrangler dev` its own `--port <self.port>`, `--inspector-port <self.ports.inspector>` and dev registry. The `inspector` port is allocated automatically. `args` that pass either port flag are refused by `devlanes config check`, and a `run_script` script that does is refused at start.",
2163
2303
  "examples": [
2164
2304
  {
2165
2305
  "args": [
@@ -2172,43 +2312,46 @@
2172
2312
  }
2173
2313
  }
2174
2314
  ],
2175
- "type": "object"
2315
+ "markdownDescription": "Options for `preset: wrangler`, which gives each lane's `wrangler dev` its own `--port <self.port>`, `--inspector-port <self.ports.inspector>` and dev registry. The `inspector` port is allocated automatically. `args` that pass either port flag are refused by `devlanes config check`, and a `run_script` script that does is refused at start."
2176
2316
  }
2177
2317
  },
2178
2318
  "type": "object"
2179
2319
  },
2180
2320
  "Started": {
2181
- "description": "Readiness without an application-level probe.",
2321
+ "description": "Readiness without a probe.",
2322
+ "enum": [
2323
+ "started"
2324
+ ],
2325
+ "enumDescriptions": [
2326
+ "Count the unit ready as soon as it runs, without probing anything. A dev server whose program crashed under a file watcher still counts as ready, so prefer a check when there is something to probe."
2327
+ ],
2182
2328
  "examples": [
2183
2329
  "started"
2184
2330
  ],
2185
- "oneOf": [
2186
- {
2187
- "const": "started",
2188
- "description": "The process has started; no TCP, HTTP, log, or command probe is needed.",
2189
- "examples": [
2190
- "started"
2191
- ],
2192
- "type": "string"
2193
- }
2194
- ]
2331
+ "markdownDescription": "Readiness without a probe.",
2332
+ "markdownEnumDescriptions": [
2333
+ "Count the unit ready as soon as it runs, without probing anything. A dev server whose program crashed under a file watcher still counts as ready, so prefer a check when there is something to probe."
2334
+ ],
2335
+ "type": "string"
2195
2336
  },
2196
2337
  "StopConfig": {
2197
2338
  "additionalProperties": false,
2198
- "description": "Signal and grace period used when stopping a process tree.",
2339
+ "description": "How devlanes stops the service on `devlanes stop`, a restart or a lane stop: it sends `signal` to every process of the service, waits up to `timeout`, then SIGKILLs whatever is still running. Defaults to SIGTERM and 10s.",
2199
2340
  "examples": [
2200
2341
  {
2201
2342
  "signal": "SIGINT",
2202
2343
  "timeout": "15s"
2203
2344
  }
2204
2345
  ],
2346
+ "markdownDescription": "How devlanes stops the service on `devlanes stop`, a restart or a lane stop: it sends `signal` to every process of the service, waits up to `timeout`, then SIGKILLs whatever is still running. Defaults to SIGTERM and 10s.",
2205
2347
  "properties": {
2206
2348
  "signal": {
2207
- "description": "Initial stop signal; defaults to SIGTERM, followed by SIGKILL if the grace period expires.",
2349
+ "description": "Signal sent to the service's whole process tree when devlanes stops or restarts it. One of `SIGTERM` (default), `SIGINT`, `SIGHUP`, `SIGQUIT`, `SIGUSR1`, `SIGUSR2` or `SIGKILL`; use `SIGINT` for programs that only shut down cleanly on Ctrl-C.",
2208
2350
  "examples": [
2209
2351
  "SIGTERM",
2210
2352
  "SIGINT"
2211
2353
  ],
2354
+ "markdownDescription": "Signal sent to the service's whole process tree when devlanes stops or restarts it. One of `SIGTERM` (default), `SIGINT`, `SIGHUP`, `SIGQUIT`, `SIGUSR1`, `SIGUSR2` or `SIGKILL`; use `SIGINT` for programs that only shut down cleanly on Ctrl-C.",
2212
2355
  "type": [
2213
2356
  "string",
2214
2357
  "null"
@@ -2217,24 +2360,28 @@
2217
2360
  "timeout": {
2218
2361
  "anyOf": [
2219
2362
  {
2220
- "$ref": "#/$defs/Templated_ConfigDuration"
2363
+ "$ref": "#/$defs/Templated_ConfigDuration",
2364
+ "description": "",
2365
+ "examples": [],
2366
+ "markdownDescription": ""
2221
2367
  },
2222
2368
  {
2223
2369
  "type": "null"
2224
2370
  }
2225
2371
  ],
2226
- "description": "Grace period before SIGKILL; defaults to 10s.",
2372
+ "description": "How long to wait after `signal` before devlanes SIGKILLs every process of the service that is still running. The service then shows as stopped; devlanes never restarts it. Defaults to 10s; raise it for programs that need time to flush or close connections.",
2227
2373
  "examples": [
2228
2374
  "15s",
2229
2375
  "10m"
2230
- ]
2376
+ ],
2377
+ "markdownDescription": "How long to wait after `signal` before devlanes SIGKILLs every process of the service that is still running. The service then shows as stopped; devlanes never restarts it. Defaults to 10s; raise it for programs that need time to flush or close connections."
2231
2378
  }
2232
2379
  },
2233
2380
  "type": "object"
2234
2381
  },
2235
2382
  "SyncConfig": {
2236
2383
  "additionalProperties": false,
2237
- "description": "Directory copied to a stable local or remote-client path.",
2384
+ "description": "Folder copied to a fixed path and kept up to date, for build output another program loads from a stable location, such as an unpacked browser extension. Also streams to machines connected with `devlanes remote connect`.",
2238
2385
  "examples": [
2239
2386
  {
2240
2387
  "from": "apps/extension/.output/chrome-mv3",
@@ -2243,44 +2390,52 @@
2243
2390
  ]
2244
2391
  }
2245
2392
  ],
2393
+ "markdownDescription": "Folder copied to a fixed path and kept up to date, for build output another program loads from a stable location, such as an unpacked browser extension. Also streams to machines connected with `devlanes remote connect`.",
2246
2394
  "properties": {
2247
2395
  "from": {
2248
2396
  "$ref": "#/$defs/Templated_string",
2249
- "description": "Directory to copy, relative to the lane's worktree.",
2397
+ "description": "Folder to copy, relative to the lane's worktree.",
2250
2398
  "examples": [
2251
2399
  "apps/extension/.output/chrome-mv3"
2252
- ]
2400
+ ],
2401
+ "markdownDescription": "Folder to copy, relative to the lane's worktree."
2253
2402
  },
2254
2403
  "ignore": {
2255
2404
  "default": [],
2256
- "description": "Glob patterns excluded from the copy and subsequent updates.",
2405
+ "description": "Glob patterns of files left out of the copy and of later updates.",
2257
2406
  "examples": [
2258
2407
  [
2259
2408
  "*.map"
2260
2409
  ]
2261
2410
  ],
2262
2411
  "items": {
2263
- "description": "Glob patterns excluded from the copy and subsequent updates.",
2412
+ "description": "Glob patterns of files left out of the copy and of later updates.",
2264
2413
  "examples": [
2265
2414
  "*.map"
2266
2415
  ],
2416
+ "markdownDescription": "Glob patterns of files left out of the copy and of later updates.",
2267
2417
  "type": "string"
2268
2418
  },
2419
+ "markdownDescription": "Glob patterns of files left out of the copy and of later updates.",
2269
2420
  "type": "array"
2270
2421
  },
2271
2422
  "to": {
2272
2423
  "anyOf": [
2273
2424
  {
2274
- "$ref": "#/$defs/Templated_string"
2425
+ "$ref": "#/$defs/Templated_string",
2426
+ "description": "",
2427
+ "examples": [],
2428
+ "markdownDescription": ""
2275
2429
  },
2276
2430
  {
2277
2431
  "type": "null"
2278
2432
  }
2279
2433
  ],
2280
- "description": "Destination on the client; defaults to a stable project/lane path under ~/devlanes/sync.",
2434
+ "description": "Local destination; `~/` is your home and other relative paths start at the worktree. Defaults to `~/devlanes/sync/<project>/<lane>/<folder>`. Machines connected with `devlanes remote connect` ignore it and use their own path.",
2281
2435
  "examples": [
2282
2436
  "~/devlanes/sync/acme/${{ lane.name }}/chrome-mv3"
2283
- ]
2437
+ ],
2438
+ "markdownDescription": "Local destination; `~/` is your home and other relative paths start at the worktree. Defaults to `~/devlanes/sync/<project>/<lane>/<folder>`. Machines connected with `devlanes remote connect` ignore it and use their own path."
2284
2439
  }
2285
2440
  },
2286
2441
  "required": [
@@ -2289,7 +2444,39 @@
2289
2444
  "type": "object"
2290
2445
  },
2291
2446
  "TagColor": {
2292
- "description": "Fixed terminal palette for tag color overrides.",
2447
+ "description": "A color from the terminal's basic palette; the exact shade follows the terminal theme, and `bright-` variants are the lighter shades.",
2448
+ "enum": [
2449
+ "red",
2450
+ "green",
2451
+ "yellow",
2452
+ "blue",
2453
+ "magenta",
2454
+ "cyan",
2455
+ "white",
2456
+ "gray",
2457
+ "bright-red",
2458
+ "bright-green",
2459
+ "bright-yellow",
2460
+ "bright-blue",
2461
+ "bright-magenta",
2462
+ "bright-cyan"
2463
+ ],
2464
+ "enumDescriptions": [
2465
+ "Show the tag's services in the terminal's red color.",
2466
+ "Show the tag's services in the terminal's green color.",
2467
+ "Show the tag's services in the terminal's yellow color.",
2468
+ "Show the tag's services in the terminal's blue color.",
2469
+ "Show the tag's services in the terminal's magenta color.",
2470
+ "Show the tag's services in the terminal's cyan color.",
2471
+ "Show the tag's services in the terminal's white color.",
2472
+ "Show the tag's services in the terminal's gray color.",
2473
+ "Show the tag's services in the terminal's bright-red color.",
2474
+ "Show the tag's services in the terminal's bright-green color.",
2475
+ "Show the tag's services in the terminal's bright-yellow color.",
2476
+ "Show the tag's services in the terminal's bright-blue color.",
2477
+ "Show the tag's services in the terminal's bright-magenta color.",
2478
+ "Show the tag's services in the terminal's bright-cyan color."
2479
+ ],
2293
2480
  "examples": [
2294
2481
  "red",
2295
2482
  "green",
@@ -2306,141 +2493,49 @@
2306
2493
  "bright-magenta",
2307
2494
  "bright-cyan"
2308
2495
  ],
2309
- "oneOf": [
2310
- {
2311
- "const": "red",
2312
- "description": "Use the terminal's red palette color.",
2313
- "examples": [
2314
- "red"
2315
- ],
2316
- "type": "string"
2317
- },
2318
- {
2319
- "const": "green",
2320
- "description": "Use the terminal's green palette color.",
2321
- "examples": [
2322
- "green"
2323
- ],
2324
- "type": "string"
2325
- },
2326
- {
2327
- "const": "yellow",
2328
- "description": "Use the terminal's yellow palette color.",
2329
- "examples": [
2330
- "yellow"
2331
- ],
2332
- "type": "string"
2333
- },
2334
- {
2335
- "const": "blue",
2336
- "description": "Use the terminal's blue palette color.",
2337
- "examples": [
2338
- "blue"
2339
- ],
2340
- "type": "string"
2341
- },
2342
- {
2343
- "const": "magenta",
2344
- "description": "Use the terminal's magenta palette color.",
2345
- "examples": [
2346
- "magenta"
2347
- ],
2348
- "type": "string"
2349
- },
2350
- {
2351
- "const": "cyan",
2352
- "description": "Use the terminal's cyan palette color.",
2353
- "examples": [
2354
- "cyan"
2355
- ],
2356
- "type": "string"
2357
- },
2358
- {
2359
- "const": "white",
2360
- "description": "Use the terminal's white palette color.",
2361
- "examples": [
2362
- "white"
2363
- ],
2364
- "type": "string"
2365
- },
2366
- {
2367
- "const": "gray",
2368
- "description": "Use the terminal's gray palette color.",
2369
- "examples": [
2370
- "gray"
2371
- ],
2372
- "type": "string"
2373
- },
2374
- {
2375
- "const": "bright-red",
2376
- "description": "Use the terminal's bright-red palette color.",
2377
- "examples": [
2378
- "bright-red"
2379
- ],
2380
- "type": "string"
2381
- },
2382
- {
2383
- "const": "bright-green",
2384
- "description": "Use the terminal's bright-green palette color.",
2385
- "examples": [
2386
- "bright-green"
2387
- ],
2388
- "type": "string"
2389
- },
2390
- {
2391
- "const": "bright-yellow",
2392
- "description": "Use the terminal's bright-yellow palette color.",
2393
- "examples": [
2394
- "bright-yellow"
2395
- ],
2396
- "type": "string"
2397
- },
2398
- {
2399
- "const": "bright-blue",
2400
- "description": "Use the terminal's bright-blue palette color.",
2401
- "examples": [
2402
- "bright-blue"
2403
- ],
2404
- "type": "string"
2405
- },
2406
- {
2407
- "const": "bright-magenta",
2408
- "description": "Use the terminal's bright-magenta palette color.",
2409
- "examples": [
2410
- "bright-magenta"
2411
- ],
2412
- "type": "string"
2413
- },
2414
- {
2415
- "const": "bright-cyan",
2416
- "description": "Use the terminal's bright-cyan palette color.",
2417
- "examples": [
2418
- "bright-cyan"
2419
- ],
2420
- "type": "string"
2421
- }
2422
- ]
2496
+ "markdownDescription": "A color from the terminal's basic palette; the exact shade follows the terminal theme, and `bright-` variants are the lighter shades.",
2497
+ "markdownEnumDescriptions": [
2498
+ "Show the tag's services in the terminal's red color.",
2499
+ "Show the tag's services in the terminal's green color.",
2500
+ "Show the tag's services in the terminal's yellow color.",
2501
+ "Show the tag's services in the terminal's blue color.",
2502
+ "Show the tag's services in the terminal's magenta color.",
2503
+ "Show the tag's services in the terminal's cyan color.",
2504
+ "Show the tag's services in the terminal's white color.",
2505
+ "Show the tag's services in the terminal's gray color.",
2506
+ "Show the tag's services in the terminal's bright-red color.",
2507
+ "Show the tag's services in the terminal's bright-green color.",
2508
+ "Show the tag's services in the terminal's bright-yellow color.",
2509
+ "Show the tag's services in the terminal's bright-blue color.",
2510
+ "Show the tag's services in the terminal's bright-magenta color.",
2511
+ "Show the tag's services in the terminal's bright-cyan color."
2512
+ ],
2513
+ "type": "string"
2423
2514
  },
2424
2515
  "TagConfig": {
2425
2516
  "additionalProperties": false,
2426
- "description": "Optional display overrides for a tag; services may also use undeclared tags.",
2517
+ "description": "Settings for one tag used in `services.<name>.tags`. Declaring a tag here is optional: services can use any tag and `+tag` selectors work without it. Declare one to pin its color or describe it.",
2427
2518
  "examples": [
2428
2519
  {
2429
2520
  "color": "cyan",
2430
2521
  "description": "HTTP APIs"
2431
2522
  }
2432
2523
  ],
2524
+ "markdownDescription": "Settings for one tag used in `services.<name>.tags`. Declaring a tag here is optional: services can use any tag and `+tag` selectors work without it. Declare one to pin its color or describe it.",
2433
2525
  "properties": {
2434
2526
  "color": {
2435
2527
  "anyOf": [
2436
2528
  {
2437
- "$ref": "#/$defs/TagColor"
2529
+ "$ref": "#/$defs/TagColor",
2530
+ "description": "",
2531
+ "examples": [],
2532
+ "markdownDescription": ""
2438
2533
  },
2439
2534
  {
2440
2535
  "type": "null"
2441
2536
  }
2442
2537
  ],
2443
- "description": "Terminal palette color; inferred by the resolver when omitted.",
2538
+ "description": "Color of services with this tag in the TUI and CLI output. When a service has several tags with a color, the first one in its `tags` list wins. Without one, devlanes infers a color from category words such as `api` (blue), `web` (green) or `worker` (magenta) in the service's tags, preset or name.",
2444
2539
  "examples": [
2445
2540
  "red",
2446
2541
  "green",
@@ -2456,14 +2551,16 @@
2456
2551
  "bright-blue",
2457
2552
  "bright-magenta",
2458
2553
  "bright-cyan"
2459
- ]
2554
+ ],
2555
+ "markdownDescription": "Color of services with this tag in the TUI and CLI output. When a service has several tags with a color, the first one in its `tags` list wins. Without one, devlanes infers a color from category words such as `api` (blue), `web` (green) or `worker` (magenta) in the service's tags, preset or name."
2460
2556
  },
2461
2557
  "description": {
2462
- "description": "Human-readable description available to the TUI.",
2558
+ "description": "Note on what the tag groups, for people reading the config.",
2463
2559
  "examples": [
2464
2560
  "HTTP APIs",
2465
2561
  "Background workers"
2466
2562
  ],
2563
+ "markdownDescription": "Note on what the tag groups, for people reading the config.",
2467
2564
  "type": [
2468
2565
  "string",
2469
2566
  "null"
@@ -2473,123 +2570,150 @@
2473
2570
  "type": "object"
2474
2571
  },
2475
2572
  "TaskAfter": {
2476
- "description": "Move a create/start task to a later startup phase.",
2573
+ "description": "Later point in the start at which a `lane.create` or `lane.start` task runs.",
2574
+ "enum": [
2575
+ "services"
2576
+ ],
2577
+ "enumDescriptions": [
2578
+ "Run once every started service is ready; the task is skipped when one of them failed or the start timed out."
2579
+ ],
2477
2580
  "examples": [
2478
2581
  "services"
2479
2582
  ],
2480
- "oneOf": [
2481
- {
2482
- "const": "services",
2483
- "description": "Run after selected services are ready; skip if any selected service failed.",
2484
- "examples": [
2485
- "services"
2486
- ],
2487
- "type": "string"
2488
- }
2489
- ]
2583
+ "markdownDescription": "Later point in the start at which a `lane.create` or `lane.start` task runs.",
2584
+ "markdownEnumDescriptions": [
2585
+ "Run once every started service is ready; the task is skipped when one of them failed or the start timed out."
2586
+ ],
2587
+ "type": "string"
2490
2588
  },
2491
2589
  "TaskConfig": {
2492
2590
  "additionalProperties": false,
2493
- "description": "One-shot command, script, or named steps, run on demand or from a lifecycle hook.",
2591
+ "description": "A one-shot command, run by hand with `devlanes run <name>` or automatically at a lane event set with `when`. Anything that ends by itself belongs here, not in `services`, where an exit counts as a failure.",
2494
2592
  "examples": [
2495
2593
  {
2496
- "run_script": "apps/api/package.json#migrate",
2594
+ "run_script": {
2595
+ "file": "apps/api/package.json",
2596
+ "name": "migrate"
2597
+ },
2497
2598
  "when": "lane.start"
2498
2599
  }
2499
2600
  ],
2601
+ "markdownDescription": "A one-shot command, run by hand with `devlanes run <name>` or automatically at a lane event set with `when`. Anything that ends by itself belongs here, not in `services`, where an exit counts as a failure.",
2500
2602
  "properties": {
2501
2603
  "after": {
2502
2604
  "anyOf": [
2503
2605
  {
2504
- "$ref": "#/$defs/TaskAfter"
2606
+ "$ref": "#/$defs/TaskAfter",
2607
+ "description": "",
2608
+ "examples": [],
2609
+ "markdownDescription": ""
2505
2610
  },
2506
2611
  {
2507
2612
  "type": "null"
2508
2613
  }
2509
2614
  ],
2510
- "description": "Delay a create/start hook until all selected services are ready.",
2615
+ "description": "Set to `services` to run a `lane.create` or `lane.start` task after the started services are ready instead of before them, for work that goes through the running app, such as seeding through its API.",
2511
2616
  "examples": [
2512
2617
  "services"
2513
- ]
2618
+ ],
2619
+ "markdownDescription": "Set to `services` to run a `lane.create` or `lane.start` task after the started services are ready instead of before them, for work that goes through the running app, such as seeding through its API."
2514
2620
  },
2515
2621
  "confirm": {
2516
2622
  "anyOf": [
2517
2623
  {
2518
- "$ref": "#/$defs/Templated_string"
2624
+ "$ref": "#/$defs/Templated_string",
2625
+ "description": "",
2626
+ "examples": [],
2627
+ "markdownDescription": ""
2519
2628
  },
2520
2629
  {
2521
2630
  "type": "null"
2522
2631
  }
2523
2632
  ],
2524
- "description": "Confirmation text; noninteractive clients must explicitly approve.",
2633
+ "description": "Question asked before the task runs, for destructive tasks. Scripts and agents without a terminal must pass `--yes` to run it.",
2525
2634
  "examples": [
2526
2635
  "Reset ${{ lane.db_name }} and re-seed it?"
2527
- ]
2636
+ ],
2637
+ "markdownDescription": "Question asked before the task runs, for destructive tasks. Scripts and agents without a terminal must pass `--yes` to run it."
2528
2638
  },
2529
2639
  "cwd": {
2530
2640
  "anyOf": [
2531
2641
  {
2532
- "$ref": "#/$defs/Templated_string"
2642
+ "$ref": "#/$defs/Templated_string",
2643
+ "description": "",
2644
+ "examples": [],
2645
+ "markdownDescription": ""
2533
2646
  },
2534
2647
  {
2535
2648
  "type": "null"
2536
2649
  }
2537
2650
  ],
2538
- "description": "Working directory relative to the lane's worktree.",
2651
+ "description": "Working directory for `run`, relative to the lane's worktree; defaults to the worktree root. Cannot be combined with `run_script`, which runs in the script file's directory.",
2539
2652
  "examples": [
2540
2653
  "apps/api"
2541
- ]
2654
+ ],
2655
+ "markdownDescription": "Working directory for `run`, relative to the lane's worktree; defaults to the worktree root. Cannot be combined with `run_script`, which runs in the script file's directory."
2542
2656
  },
2543
2657
  "env": {
2544
2658
  "additionalProperties": {
2545
2659
  "$ref": "#/$defs/Templated_Scalar",
2546
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
2660
+ "description": "Variables for this task only, above `global_env`.",
2547
2661
  "examples": [
2548
- "${{ lane.index }}"
2549
- ]
2662
+ "${{ services.api.url }}"
2663
+ ],
2664
+ "markdownDescription": "Variables for this task only, above `global_env`."
2550
2665
  },
2551
2666
  "default": {},
2552
- "description": "Task-specific environment overrides.",
2667
+ "description": "Variables for this task only, above `global_env`.",
2553
2668
  "examples": [
2554
2669
  {
2555
- "API_URL": "${{ svc.api.url }}"
2670
+ "API_URL": "${{ services.api.url }}"
2556
2671
  }
2557
2672
  ],
2673
+ "markdownDescription": "Variables for this task only, above `global_env`.",
2558
2674
  "type": "object"
2559
2675
  },
2560
2676
  "fnox": {
2561
2677
  "anyOf": [
2562
2678
  {
2563
- "$ref": "#/$defs/FnoxConfig"
2679
+ "$ref": "#/$defs/FnoxConfig",
2680
+ "description": "",
2681
+ "examples": [],
2682
+ "markdownDescription": ""
2564
2683
  },
2565
2684
  {
2566
2685
  "type": "null"
2567
2686
  }
2568
2687
  ],
2569
- "description": "Task-specific fnox profile.",
2688
+ "description": "fnox profile for this task's secrets, instead of the project's `fnox.profile`.",
2570
2689
  "examples": [
2571
2690
  {
2572
2691
  "profile": "dev"
2573
2692
  }
2574
- ]
2693
+ ],
2694
+ "markdownDescription": "fnox profile for this task's secrets, instead of the project's `fnox.profile`."
2575
2695
  },
2576
2696
  "interactive": {
2577
2697
  "anyOf": [
2578
2698
  {
2579
- "$ref": "#/$defs/Templated_boolean"
2699
+ "$ref": "#/$defs/Templated_boolean",
2700
+ "description": "",
2701
+ "examples": [],
2702
+ "markdownDescription": ""
2580
2703
  },
2581
2704
  {
2582
2705
  "type": "null"
2583
2706
  }
2584
2707
  ],
2585
- "description": "Require an attached terminal; incompatible with lifecycle hooks.",
2708
+ "description": "Give the task your terminal for input and output, for logins, prompts and TUIs. Interactive tasks only run by hand, so they cannot have `when`. Defaults to false.",
2586
2709
  "examples": [
2587
2710
  true,
2588
2711
  false
2589
- ]
2712
+ ],
2713
+ "markdownDescription": "Give the task your terminal for input and output, for logins, prompts and TUIs. Interactive tasks only run by hand, so they cannot have `when`. Defaults to false."
2590
2714
  },
2591
2715
  "profiles": {
2592
- "description": "Profiles for which this lifecycle hook runs automatically.",
2716
+ "description": "Run this task's `when` hook only while one of these profiles is active; `default` also matches a start without `--profile`. Runs by hand are not affected. Requires `when`.",
2593
2717
  "examples": [
2594
2718
  [
2595
2719
  "default",
@@ -2597,13 +2721,15 @@
2597
2721
  ]
2598
2722
  ],
2599
2723
  "items": {
2600
- "description": "Profiles for which this lifecycle hook runs automatically.",
2724
+ "description": "Run this task's `when` hook only while one of these profiles is active; `default` also matches a start without `--profile`. Runs by hand are not affected. Requires `when`.",
2601
2725
  "examples": [
2602
2726
  "default",
2603
2727
  "backend"
2604
2728
  ],
2729
+ "markdownDescription": "Run this task's `when` hook only while one of these profiles is active; `default` also matches a start without `--profile`. Runs by hand are not affected. Requires `when`.",
2605
2730
  "type": "string"
2606
2731
  },
2732
+ "markdownDescription": "Run this task's `when` hook only while one of these profiles is active; `default` also matches a start without `--profile`. Runs by hand are not affected. Requires `when`.",
2607
2733
  "type": [
2608
2734
  "array",
2609
2735
  "null"
@@ -2612,28 +2738,35 @@
2612
2738
  "pty": {
2613
2739
  "anyOf": [
2614
2740
  {
2615
- "$ref": "#/$defs/Templated_boolean"
2741
+ "$ref": "#/$defs/Templated_boolean",
2742
+ "description": "",
2743
+ "examples": [],
2744
+ "markdownDescription": ""
2616
2745
  },
2617
2746
  {
2618
2747
  "type": "null"
2619
2748
  }
2620
2749
  ],
2621
- "description": "Allocate a pseudoterminal instead of pipes.",
2750
+ "description": "Run the task in a pseudoterminal instead of plain pipes, for tools that only print colors or progress to a terminal. Defaults to the value of `interactive`.",
2622
2751
  "examples": [
2623
2752
  true,
2624
2753
  false
2625
- ]
2754
+ ],
2755
+ "markdownDescription": "Run the task in a pseudoterminal instead of plain pipes, for tools that only print colors or progress to a terminal. Defaults to the value of `interactive`."
2626
2756
  },
2627
2757
  "run": {
2628
2758
  "anyOf": [
2629
2759
  {
2630
- "$ref": "#/$defs/Run"
2760
+ "$ref": "#/$defs/Run",
2761
+ "description": "",
2762
+ "examples": [],
2763
+ "markdownDescription": ""
2631
2764
  },
2632
2765
  {
2633
2766
  "type": "null"
2634
2767
  }
2635
2768
  ],
2636
- "description": "Shell text or an argument vector. Mutually exclusive with run_script and steps.",
2769
+ "description": "Command to run: a string runs through `/bin/sh -c` (pipes and multi-line scripts work), a list runs the program directly with those arguments. Use exactly one of `run`, `run_script` or `steps`.",
2637
2770
  "examples": [
2638
2771
  "pnpm dev",
2639
2772
  [
@@ -2642,28 +2775,36 @@
2642
2775
  "--port",
2643
2776
  "${{ self.port }}"
2644
2777
  ]
2645
- ]
2778
+ ],
2779
+ "markdownDescription": "Command to run: a string runs through `/bin/sh -c` (pipes and multi-line scripts work), a list runs the program directly with those arguments. Use exactly one of `run`, `run_script` or `steps`."
2646
2780
  },
2647
2781
  "run_script": {
2648
2782
  "anyOf": [
2649
2783
  {
2650
- "$ref": "#/$defs/RunScript"
2784
+ "$ref": "#/$defs/RunScript",
2785
+ "description": "",
2786
+ "examples": [],
2787
+ "markdownDescription": ""
2651
2788
  },
2652
2789
  {
2653
2790
  "type": "null"
2654
2791
  }
2655
2792
  ],
2656
- "description": "Script source and name, such as package.json#migrate or Makefile#build.",
2793
+ "description": "Script to run from a project file, as `{ file, name }`, such as `{ file: apps/api/package.json, name: migrate }`. It runs with that file's tool (the project's package manager for package.json) in the file's directory, and a missing script fails before anything starts. Prefer it to `run` for scripts that live in a file. Use exactly one of `run`, `run_script` or `steps`.",
2657
2794
  "examples": [
2658
- "apps/api/package.json#dev",
2659
2795
  {
2660
- "name": "fixtures",
2661
- "source": "justfile"
2796
+ "file": "apps/api/package.json",
2797
+ "name": "dev"
2798
+ },
2799
+ {
2800
+ "file": "justfile",
2801
+ "name": "fixtures"
2662
2802
  }
2663
- ]
2803
+ ],
2804
+ "markdownDescription": "Script to run from a project file, as `{ file, name }`, such as `{ file: apps/api/package.json, name: migrate }`. It runs with that file's tool (the project's package manager for package.json) in the file's directory, and a missing script fails before anything starts. Prefer it to `run` for scripts that live in a file. Use exactly one of `run`, `run_script` or `steps`."
2664
2805
  },
2665
2806
  "steps": {
2666
- "description": "Sequential task names or infra.setup/infra.teardown entries, always run explicitly.",
2807
+ "description": "Run these tasks and `infra.setup.<name>` or `infra.teardown.<name>` entries in order, stopping at the first failure, for jobs such as resetting a database. Each step runs whatever its own `when` says, and setup entries run again even when already done. Use instead of `run` or `run_script`.",
2667
2808
  "examples": [
2668
2809
  [
2669
2810
  "infra.teardown.database",
@@ -2673,15 +2814,17 @@
2673
2814
  ]
2674
2815
  ],
2675
2816
  "items": {
2676
- "description": "Sequential task names or infra.setup/infra.teardown entries, always run explicitly.",
2817
+ "description": "Run these tasks and `infra.setup.<name>` or `infra.teardown.<name>` entries in order, stopping at the first failure, for jobs such as resetting a database. Each step runs whatever its own `when` says, and setup entries run again even when already done. Use instead of `run` or `run_script`.",
2677
2818
  "examples": [
2678
2819
  "infra.teardown.database",
2679
2820
  "infra.setup.database",
2680
2821
  "migrate",
2681
2822
  "seed"
2682
2823
  ],
2824
+ "markdownDescription": "Run these tasks and `infra.setup.<name>` or `infra.teardown.<name>` entries in order, stopping at the first failure, for jobs such as resetting a database. Each step runs whatever its own `when` says, and setup entries run again even when already done. Use instead of `run` or `run_script`.",
2683
2825
  "type": "string"
2684
2826
  },
2827
+ "markdownDescription": "Run these tasks and `infra.setup.<name>` or `infra.teardown.<name>` entries in order, stopping at the first failure, for jobs such as resetting a database. Each step runs whatever its own `when` says, and setup entries run again even when already done. Use instead of `run` or `run_script`.",
2685
2828
  "type": [
2686
2829
  "array",
2687
2830
  "null"
@@ -2690,276 +2833,255 @@
2690
2833
  "timeout": {
2691
2834
  "anyOf": [
2692
2835
  {
2693
- "$ref": "#/$defs/Templated_ConfigDuration"
2836
+ "$ref": "#/$defs/Templated_ConfigDuration",
2837
+ "description": "",
2838
+ "examples": [],
2839
+ "markdownDescription": ""
2694
2840
  },
2695
2841
  {
2696
2842
  "type": "null"
2697
2843
  }
2698
2844
  ],
2699
- "description": "Maximum run duration, for example 10m.",
2845
+ "description": "Longest the task may run. When it runs out, the task is stopped (SIGTERM, then SIGKILL after 10 seconds) and counts as failed; a failed task that runs before services fails the start. No limit by default.",
2700
2846
  "examples": [
2701
2847
  "15s",
2702
2848
  "10m"
2703
- ]
2849
+ ],
2850
+ "markdownDescription": "Longest the task may run. When it runs out, the task is stopped (SIGTERM, then SIGKILL after 10 seconds) and counts as failed; a failed task that runs before services fails the start. No limit by default."
2704
2851
  },
2705
2852
  "when": {
2706
2853
  "anyOf": [
2707
2854
  {
2708
- "$ref": "#/$defs/TaskWhen"
2855
+ "$ref": "#/$defs/TaskWhen",
2856
+ "description": "",
2857
+ "examples": [],
2858
+ "markdownDescription": ""
2709
2859
  },
2710
2860
  {
2711
2861
  "type": "null"
2712
2862
  }
2713
2863
  ],
2714
- "description": "Lifecycle hook; omitted tasks run only on demand.",
2864
+ "description": "Lane event that runs this task automatically. Without it the task runs only by hand with `devlanes run <name>` or as a step of another task.",
2715
2865
  "examples": [
2716
2866
  "lane.create",
2717
2867
  "lane.start",
2718
2868
  "lane.stop",
2719
2869
  "lane.clean"
2720
- ]
2870
+ ],
2871
+ "markdownDescription": "Lane event that runs this task automatically. Without it the task runs only by hand with `devlanes run <name>` or as a step of another task."
2721
2872
  }
2722
2873
  },
2723
2874
  "type": "object"
2724
2875
  },
2725
2876
  "TaskWhen": {
2726
- "description": "Lifecycle event that runs a task automatically.",
2877
+ "description": "Lane event that runs a task automatically.",
2878
+ "enum": [
2879
+ "lane.create",
2880
+ "lane.start",
2881
+ "lane.stop",
2882
+ "lane.clean"
2883
+ ],
2884
+ "enumDescriptions": [
2885
+ "Run on each start until it has succeeded once for the lane, then no longer automatically; for installs, code generation and a first seed.",
2886
+ "Run on every start of the lane, before services unless `after: services` is set; for migrations and other steps that are safe to repeat.",
2887
+ "Run before the lane's processes stop when the whole lane stops: `devlanes stop` without unit names, `devlanes daemon stop` and `daemon restart`. Skipped when a signal (logout, system shutdown) stops the daemon.",
2888
+ "Run when the lane is cleaned (`devlanes lane clean`), after its processes stop and before `infra.teardown`."
2889
+ ],
2727
2890
  "examples": [
2728
2891
  "lane.create",
2729
2892
  "lane.start",
2730
2893
  "lane.stop",
2731
2894
  "lane.clean"
2732
2895
  ],
2733
- "oneOf": [
2734
- {
2735
- "const": "lane.create",
2736
- "description": "Run until the task succeeds once for this lane.",
2737
- "examples": [
2738
- "lane.create"
2739
- ],
2740
- "type": "string"
2741
- },
2742
- {
2743
- "const": "lane.start",
2744
- "description": "Run each time the lane starts.",
2745
- "examples": [
2746
- "lane.start"
2747
- ],
2748
- "type": "string"
2749
- },
2750
- {
2751
- "const": "lane.stop",
2752
- "description": "Run when the whole lane stops, on `devlanes daemon stop` and on `daemon restart`; skipped when a signal (logout, system shutdown) stops the daemon.",
2753
- "examples": [
2754
- "lane.stop"
2755
- ],
2756
- "type": "string"
2757
- },
2758
- {
2759
- "const": "lane.clean",
2760
- "description": "Run during lane cleanup before infrastructure teardown.",
2761
- "examples": [
2762
- "lane.clean"
2763
- ],
2764
- "type": "string"
2765
- }
2766
- ]
2896
+ "markdownDescription": "Lane event that runs a task automatically.",
2897
+ "markdownEnumDescriptions": [
2898
+ "Run on each start until it has succeeded once for the lane, then no longer automatically; for installs, code generation and a first seed.",
2899
+ "Run on every start of the lane, before services unless `after: services` is set; for migrations and other steps that are safe to repeat.",
2900
+ "Run before the lane's processes stop when the whole lane stops: `devlanes stop` without unit names, `devlanes daemon stop` and `daemon restart`. Skipped when a signal (logout, system shutdown) stops the daemon.",
2901
+ "Run when the lane is cleaned (`devlanes lane clean`), after its processes stop and before `infra.teardown`."
2902
+ ],
2903
+ "type": "string"
2767
2904
  },
2768
2905
  "TcpReady": {
2769
2906
  "anyOf": [
2770
2907
  {
2771
- "description": "Set true to probe the unit's primary port; false is invalid.",
2772
- "examples": [
2773
- true
2774
- ],
2775
2908
  "type": "boolean"
2776
2909
  },
2777
2910
  {
2778
- "description": "Probe a named extra port instead of the primary port.",
2779
- "examples": [
2780
- "inspector"
2781
- ],
2782
2911
  "type": "string"
2783
2912
  }
2784
2913
  ],
2785
- "description": "TCP port to probe for readiness.",
2914
+ "description": "Port a `tcp` readiness check connects to: `true` for the main port, or a port name.",
2786
2915
  "examples": [
2787
2916
  true,
2788
2917
  "inspector"
2789
- ]
2918
+ ],
2919
+ "markdownDescription": "Port a `tcp` readiness check connects to: `true` for the main port, or a port name."
2790
2920
  },
2791
2921
  "Templated_ConfigDuration": {
2792
2922
  "anyOf": [
2793
2923
  {
2794
2924
  "$ref": "#/$defs/ConfigDuration",
2795
- "description": "Literal value used without expression evaluation."
2925
+ "description": "",
2926
+ "examples": [],
2927
+ "markdownDescription": ""
2796
2928
  },
2797
2929
  {
2798
- "description": "Text containing `${{ ... }}`, evaluated for each lane; write `${{ '${{' }}` for a literal `${{`.",
2799
- "examples": [
2800
- "${{ lane.index }}"
2801
- ],
2802
2930
  "pattern": "\\$\\{\\{",
2803
2931
  "type": "string"
2804
2932
  }
2805
2933
  ],
2806
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
2934
+ "description": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`.",
2807
2935
  "examples": [
2808
2936
  "${{ lane.index }}"
2809
- ]
2937
+ ],
2938
+ "markdownDescription": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`."
2810
2939
  },
2811
2940
  "Templated_Scalar": {
2812
2941
  "anyOf": [
2813
2942
  {
2814
2943
  "$ref": "#/$defs/Scalar",
2815
- "description": "Literal value used without expression evaluation."
2944
+ "description": "",
2945
+ "examples": [],
2946
+ "markdownDescription": ""
2816
2947
  },
2817
2948
  {
2818
- "description": "Text containing `${{ ... }}`, evaluated for each lane; write `${{ '${{' }}` for a literal `${{`.",
2819
- "examples": [
2820
- "${{ lane.index }}"
2821
- ],
2822
2949
  "pattern": "\\$\\{\\{",
2823
2950
  "type": "string"
2824
2951
  }
2825
2952
  ],
2826
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
2953
+ "description": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`.",
2827
2954
  "examples": [
2828
2955
  "${{ lane.index }}"
2829
- ]
2956
+ ],
2957
+ "markdownDescription": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`."
2830
2958
  },
2831
2959
  "Templated_boolean": {
2832
2960
  "anyOf": [
2833
2961
  {
2834
- "description": "Literal value used without expression evaluation.",
2835
2962
  "type": "boolean"
2836
2963
  },
2837
2964
  {
2838
- "description": "Text containing `${{ ... }}`, evaluated for each lane; write `${{ '${{' }}` for a literal `${{`.",
2839
- "examples": [
2840
- "${{ lane.index }}"
2841
- ],
2842
2965
  "pattern": "\\$\\{\\{",
2843
2966
  "type": "string"
2844
2967
  }
2845
2968
  ],
2846
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
2969
+ "description": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`.",
2847
2970
  "examples": [
2848
2971
  "${{ lane.index }}"
2849
- ]
2972
+ ],
2973
+ "markdownDescription": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`."
2850
2974
  },
2851
2975
  "Templated_string": {
2852
2976
  "anyOf": [
2853
2977
  {
2854
- "description": "Literal value used without expression evaluation.",
2855
2978
  "type": "string"
2856
2979
  },
2857
2980
  {
2858
- "description": "Text containing `${{ ... }}`, evaluated for each lane; write `${{ '${{' }}` for a literal `${{`.",
2859
- "examples": [
2860
- "${{ lane.index }}"
2861
- ],
2862
2981
  "pattern": "\\$\\{\\{",
2863
2982
  "type": "string"
2864
2983
  }
2865
2984
  ],
2866
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
2985
+ "description": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`.",
2867
2986
  "examples": [
2868
2987
  "${{ lane.index }}"
2869
- ]
2988
+ ],
2989
+ "markdownDescription": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`."
2870
2990
  },
2871
2991
  "Templated_uint16": {
2872
2992
  "anyOf": [
2873
2993
  {
2874
- "description": "Literal value used without expression evaluation.",
2875
2994
  "format": "uint16",
2876
2995
  "maximum": 65535,
2877
2996
  "minimum": 0,
2878
2997
  "type": "integer"
2879
2998
  },
2880
2999
  {
2881
- "description": "Text containing `${{ ... }}`, evaluated for each lane; write `${{ '${{' }}` for a literal `${{`.",
2882
- "examples": [
2883
- "${{ lane.index }}"
2884
- ],
2885
3000
  "pattern": "\\$\\{\\{",
2886
3001
  "type": "string"
2887
3002
  }
2888
3003
  ],
2889
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
3004
+ "description": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`.",
2890
3005
  "examples": [
2891
3006
  "${{ lane.index }}"
2892
- ]
3007
+ ],
3008
+ "markdownDescription": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`."
2893
3009
  },
2894
3010
  "Templated_uint32": {
2895
3011
  "anyOf": [
2896
3012
  {
2897
- "description": "Literal value used without expression evaluation.",
2898
3013
  "format": "uint32",
2899
3014
  "minimum": 0,
2900
3015
  "type": "integer"
2901
3016
  },
2902
3017
  {
2903
- "description": "Text containing `${{ ... }}`, evaluated for each lane; write `${{ '${{' }}` for a literal `${{`.",
2904
- "examples": [
2905
- "${{ lane.index }}"
2906
- ],
2907
3018
  "pattern": "\\$\\{\\{",
2908
3019
  "type": "string"
2909
3020
  }
2910
3021
  ],
2911
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
3022
+ "description": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`.",
2912
3023
  "examples": [
2913
3024
  "${{ lane.index }}"
2914
- ]
3025
+ ],
3026
+ "markdownDescription": "A plain value or text with `${{ ... }}` expressions, evaluated for each lane with names such as `lane`, `self`, `services`, `infra` and `env`. A value that is one whole expression keeps its type, which must fit the setting. Write `${{ '${{' }}` for a literal `${{`."
2915
3027
  }
2916
3028
  },
2917
3029
  "$id": "https://devlanes.run/v1/schema.json",
2918
3030
  "$schema": "https://json-schema.org/draft/2020-12/schema",
2919
3031
  "additionalProperties": false,
2920
- "description": "Fully merged project configuration. Map order is declaration order.",
3032
+ "description": "devlanes v0.3.0 project config. Run a dev environment per git worktree, side by side. https://devlanes.run",
2921
3033
  "examples": [
2922
3034
  {
2923
3035
  "project": "acme",
2924
3036
  "services": {
2925
3037
  "api": {
2926
- "run_script": "apps/api/package.json#dev"
3038
+ "run_script": {
3039
+ "file": "apps/api/package.json",
3040
+ "name": "dev"
3041
+ }
2927
3042
  }
2928
3043
  }
2929
3044
  }
2930
3045
  ],
3046
+ "markdownDescription": "devlanes v0.3.0 project config. Run a dev environment per git worktree, side by side. https://devlanes.run",
2931
3047
  "properties": {
2932
3048
  "fnox": {
2933
3049
  "anyOf": [
2934
3050
  {
2935
- "$ref": "#/$defs/FnoxConfig"
3051
+ "$ref": "#/$defs/FnoxConfig",
3052
+ "description": "",
3053
+ "examples": [],
3054
+ "markdownDescription": ""
2936
3055
  },
2937
3056
  {
2938
3057
  "type": "null"
2939
3058
  }
2940
3059
  ],
2941
- "description": "Project-wide fnox profile for secrets resolved by the runtime.",
3060
+ "description": "fnox profile that the secrets of every task and service come from. A task or service can name its own profile with its `fnox` key.",
2942
3061
  "examples": [
2943
3062
  {
2944
3063
  "profile": "dev"
2945
3064
  }
2946
- ]
3065
+ ],
3066
+ "markdownDescription": "fnox profile that the secrets of every task and service come from. A task or service can name its own profile with its `fnox` key."
2947
3067
  },
2948
3068
  "global_env": {
2949
3069
  "additionalProperties": {
2950
3070
  "$ref": "#/$defs/Templated_Scalar",
2951
- "description": "A literal value or a `${{ ... }}` expression, checked for the required type after evaluation.\nWrite `${{ '${{' }}` for a literal `${{`.",
3071
+ "description": "Variables for every service and task of the lane, above devlanes' own variables (`PORT`, `DEVLANES_*`) and below each unit's `env` and the profile's `env`. Use it for values many units share, such as a database URL built from `${{ infra.postgres.port }}`. Never set `PORT` here: it would replace every service's own port.",
2952
3072
  "examples": [
2953
- "${{ lane.index }}"
2954
- ]
3073
+ "${{ services.api.url }}"
3074
+ ],
3075
+ "markdownDescription": "Variables for every service and task of the lane, above devlanes' own variables (`PORT`, `DEVLANES_*`) and below each unit's `env` and the profile's `env`. Use it for values many units share, such as a database URL built from `${{ infra.postgres.port }}`. Never set `PORT` here: it would replace every service's own port."
2955
3076
  },
2956
3077
  "default": {},
2957
- "description": "Environment overrides for tasks and services, after derived DEVLANES values.",
3078
+ "description": "Variables for every service and task of the lane, above devlanes' own variables (`PORT`, `DEVLANES_*`) and below each unit's `env` and the profile's `env`. Use it for values many units share, such as a database URL built from `${{ infra.postgres.port }}`. Never set `PORT` here: it would replace every service's own port.",
2958
3079
  "examples": [
2959
3080
  {
2960
- "API_URL": "${{ svc.api.url }}"
3081
+ "API_URL": "${{ services.api.url }}"
2961
3082
  }
2962
3083
  ],
3084
+ "markdownDescription": "Variables for every service and task of the lane, above devlanes' own variables (`PORT`, `DEVLANES_*`) and below each unit's `env` and the profile's `env`. Use it for values many units share, such as a database URL built from `${{ infra.postgres.port }}`. Never set `PORT` here: it would replace every service's own port.",
2963
3085
  "type": "object"
2964
3086
  },
2965
3087
  "infra": {
@@ -2977,7 +3099,7 @@
2977
3099
  "setup": {},
2978
3100
  "teardown": {}
2979
3101
  },
2980
- "description": "Shared and per-lane containers, setup commands, and teardown commands.",
3102
+ "description": "Containers the lanes depend on (databases, brokers, object stores), started and waited on before setup, tasks and services, plus the commands that create and remove each lane's data in them.",
2981
3103
  "examples": [
2982
3104
  {
2983
3105
  "compose": "docker-compose.yml",
@@ -2985,7 +3107,8 @@
2985
3107
  "postgres"
2986
3108
  ]
2987
3109
  }
2988
- ]
3110
+ ],
3111
+ "markdownDescription": "Containers the lanes depend on (databases, brokers, object stores), started and waited on before setup, tasks and services, plus the commands that create and remove each lane's data in them."
2989
3112
  },
2990
3113
  "lanes": {
2991
3114
  "$ref": "#/$defs/LanesConfig",
@@ -3000,7 +3123,7 @@
3000
3123
  "vars": {},
3001
3124
  "worktree_dir": null
3002
3125
  },
3003
- "description": "Worktree registration, per-lane variables, and port-block policy.",
3126
+ "description": "Which worktrees get a lane, where new worktrees are created, how many ports each lane reserves, and values computed per lane (`lane.<name>`).",
3004
3127
  "examples": [
3005
3128
  {
3006
3129
  "hosts_file": "always",
@@ -3009,12 +3132,13 @@
3009
3132
  "db_name": "acme_${{ lane.name }}"
3010
3133
  }
3011
3134
  }
3012
- ]
3135
+ ],
3136
+ "markdownDescription": "Which worktrees get a lane, where new worktrees are created, how many ports each lane reserves, and values computed per lane (`lane.<name>`)."
3013
3137
  },
3014
3138
  "profiles": {
3015
3139
  "additionalProperties": {
3016
3140
  "$ref": "#/$defs/ProfileConfig",
3017
- "description": "Named service selection with optional environment, infrastructure, and script overrides.",
3141
+ "description": "Services started by `devlanes start --profile <name>` (`default` when no profile is given): a list of names, globs and `+tag` selectors, or a mapping that can also exclude services, skip infra, add `env` and remap script names. Several `--profile` flags start the union.",
3018
3142
  "examples": [
3019
3143
  [
3020
3144
  "+api",
@@ -3026,10 +3150,11 @@
3026
3150
  ],
3027
3151
  "infra": false
3028
3152
  }
3029
- ]
3153
+ ],
3154
+ "markdownDescription": "Services started by `devlanes start --profile <name>` (`default` when no profile is given): a list of names, globs and `+tag` selectors, or a mapping that can also exclude services, skip infra, add `env` and remap script names. Several `--profile` flags start the union."
3030
3155
  },
3031
3156
  "default": {},
3032
- "description": "Named service selectors with optional environment and infrastructure overrides.",
3157
+ "description": "Named selections of services for `devlanes start --profile <name>`; `default` is used when no profile is given. A profile is a list of names, globs and `+tag` selectors, or a mapping that can also exclude services, skip infra, add `env` and remap script names.",
3033
3158
  "examples": [
3034
3159
  {
3035
3160
  "default": [
@@ -3046,58 +3171,65 @@
3046
3171
  }
3047
3172
  }
3048
3173
  ],
3174
+ "markdownDescription": "Named selections of services for `devlanes start --profile <name>`; `default` is used when no profile is given. A profile is a list of names, globs and `+tag` selectors, or a mapping that can also exclude services, skip infra, add `env` and remap script names.",
3049
3175
  "type": "object"
3050
3176
  },
3051
3177
  "project": {
3052
- "description": "Project identity shared by every worktree; lowercase letters, digits, dots, underscores, and dashes.",
3178
+ "description": "Project name, the same in every worktree of the repository. It builds each lane's default domain (`<project>.localhost` for lane 0, `<project>-<N>.localhost` for lane N), names lanes in commands (`acme@2`), the containers devlanes creates and `DEVLANES_PROJECT`, and other projects reach this one as `projects.<project>`. Lowercase letters, digits, dots, underscores and dashes.",
3053
3179
  "examples": [
3054
3180
  "acme"
3055
3181
  ],
3182
+ "markdownDescription": "Project name, the same in every worktree of the repository. It builds each lane's default domain (`<project>.localhost` for lane 0, `<project>-<N>.localhost` for lane N), names lanes in commands (`acme@2`), the containers devlanes creates and `DEVLANES_PROJECT`, and other projects reach this one as `projects.<project>`. Lowercase letters, digits, dots, underscores and dashes.",
3056
3183
  "type": "string"
3057
3184
  },
3058
3185
  "services": {
3059
3186
  "additionalProperties": {
3060
3187
  "$ref": "#/$defs/ServiceConfig",
3061
- "description": "Service settings after typed local and template merging.",
3188
+ "description": "A long-running process started with the lane, such as a dev server or a worker. Each service gets its own port from the lane's block (`PORT`, `${{ self.port }}`); devlanes stops it with the lane and never restarts it when it exits. A service needs `run` or `run_script`, directly or through `extends`.",
3062
3189
  "examples": [
3063
3190
  {
3064
- "hosts": [
3065
- "api.${{ lane.domain }}"
3066
- ],
3067
- "run_script": "apps/api/package.json#dev",
3191
+ "hosts": "api.${{ lane.domain }}",
3192
+ "run_script": {
3193
+ "file": "apps/api/package.json",
3194
+ "name": "dev"
3195
+ },
3068
3196
  "tags": [
3069
3197
  "api"
3070
3198
  ]
3071
3199
  }
3072
- ]
3200
+ ],
3201
+ "markdownDescription": "A long-running process started with the lane, such as a dev server or a worker. Each service gets its own port from the lane's block (`PORT`, `${{ self.port }}`); devlanes stops it with the lane and never restarts it when it exits. A service needs `run` or `run_script`, directly or through `extends`."
3073
3202
  },
3074
3203
  "default": {},
3075
- "description": "Long-running processes started together after infrastructure and before-service tasks.",
3204
+ "description": "Long-running processes of each lane, started together after infrastructure, setup and `lane.start` tasks. Each service gets its own port as `PORT` and, with `hosts`, HTTPS hostnames. A service that exits shows as failed and is not restarted.",
3076
3205
  "examples": [
3077
3206
  {
3078
3207
  "api": {
3079
- "hosts": [
3080
- "api.${{ lane.domain }}"
3081
- ],
3082
- "run_script": "apps/api/package.json#dev"
3208
+ "hosts": "api.${{ lane.domain }}",
3209
+ "run_script": {
3210
+ "file": "apps/api/package.json",
3211
+ "name": "dev"
3212
+ }
3083
3213
  }
3084
3214
  }
3085
3215
  ],
3216
+ "markdownDescription": "Long-running processes of each lane, started together after infrastructure, setup and `lane.start` tasks. Each service gets its own port as `PORT` and, with `hosts`, HTTPS hostnames. A service that exits shows as failed and is not restarted.",
3086
3217
  "type": "object"
3087
3218
  },
3088
3219
  "tags": {
3089
3220
  "additionalProperties": {
3090
3221
  "$ref": "#/$defs/TagConfig",
3091
- "description": "Optional display overrides for a tag; services may also use undeclared tags.",
3222
+ "description": "Settings for one tag used in `services.<name>.tags`. Declaring a tag here is optional: services can use any tag and `+tag` selectors work without it. Declare one to pin its color or describe it.",
3092
3223
  "examples": [
3093
3224
  {
3094
3225
  "color": "cyan",
3095
3226
  "description": "HTTP APIs"
3096
3227
  }
3097
- ]
3228
+ ],
3229
+ "markdownDescription": "Settings for one tag used in `services.<name>.tags`. Declaring a tag here is optional: services can use any tag and `+tag` selectors work without it. Declare one to pin its color or describe it."
3098
3230
  },
3099
3231
  "default": {},
3100
- "description": "Tag settings. Units are colored by their first tag; colors are inferred from common tag names (api, web, worker, db, ...) when not set here.",
3232
+ "description": "Optional color and description per tag. Services list their tags in their own `tags`, and tags group them: `+api` selects every service tagged `api` in profiles and in commands such as `devlanes start +api`. A unit's color comes from the first of its tags with a `color` set here, else the first tag naming a category such as api, web, worker or db, else its preset or name. Tags not listed here work too.",
3101
3233
  "examples": [
3102
3234
  {
3103
3235
  "api": {
@@ -3106,49 +3238,60 @@
3106
3238
  }
3107
3239
  }
3108
3240
  ],
3241
+ "markdownDescription": "Optional color and description per tag. Services list their tags in their own `tags`, and tags group them: `+api` selects every service tagged `api` in profiles and in commands such as `devlanes start +api`. A unit's color comes from the first of its tags with a `color` set here, else the first tag naming a category such as api, web, worker or db, else its preset or name. Tags not listed here work too.",
3109
3242
  "type": "object"
3110
3243
  },
3111
3244
  "tasks": {
3112
3245
  "additionalProperties": {
3113
3246
  "$ref": "#/$defs/TaskConfig",
3114
- "description": "One-shot command, script, or named steps, run on demand or from a lifecycle hook.",
3247
+ "description": "A one-shot command, run by hand with `devlanes run <name>` or automatically at a lane event set with `when`. Anything that ends by itself belongs here, not in `services`, where an exit counts as a failure.",
3115
3248
  "examples": [
3116
3249
  {
3117
- "run_script": "apps/api/package.json#migrate",
3250
+ "run_script": {
3251
+ "file": "apps/api/package.json",
3252
+ "name": "migrate"
3253
+ },
3118
3254
  "when": "lane.start"
3119
3255
  }
3120
- ]
3256
+ ],
3257
+ "markdownDescription": "A one-shot command, run by hand with `devlanes run <name>` or automatically at a lane event set with `when`. Anything that ends by itself belongs here, not in `services`, where an exit counts as a failure."
3121
3258
  },
3122
3259
  "default": {},
3123
- "description": "One-shot commands; lifecycle hooks execute in declaration order.",
3260
+ "description": "One-shot commands such as installs, migrations and seeds, run by hand with `devlanes run <name>` or automatically at a lane event set with `when`. Tasks of the same event run in declaration order.",
3124
3261
  "examples": [
3125
3262
  {
3126
3263
  "migrate": {
3127
- "run_script": "apps/api/package.json#migrate",
3264
+ "run_script": {
3265
+ "file": "apps/api/package.json",
3266
+ "name": "migrate"
3267
+ },
3128
3268
  "when": "lane.start"
3129
3269
  }
3130
3270
  }
3131
3271
  ],
3272
+ "markdownDescription": "One-shot commands such as installs, migrations and seeds, run by hand with `devlanes run <name>` or automatically at a lane event set with `when`. Tasks of the same event run in declaration order.",
3132
3273
  "type": "object"
3133
3274
  },
3134
3275
  "templates": {
3135
3276
  "additionalProperties": {
3136
3277
  "$ref": "#/$defs/ServiceConfig",
3137
- "description": "Service settings after typed local and template merging.",
3278
+ "description": "A long-running process started with the lane, such as a dev server or a worker. Each service gets its own port from the lane's block (`PORT`, `${{ self.port }}`); devlanes stops it with the lane and never restarts it when it exits. A service needs `run` or `run_script`, directly or through `extends`.",
3138
3279
  "examples": [
3139
3280
  {
3140
- "hosts": [
3141
- "api.${{ lane.domain }}"
3142
- ],
3143
- "run_script": "apps/api/package.json#dev",
3281
+ "hosts": "api.${{ lane.domain }}",
3282
+ "run_script": {
3283
+ "file": "apps/api/package.json",
3284
+ "name": "dev"
3285
+ },
3144
3286
  "tags": [
3145
3287
  "api"
3146
3288
  ]
3147
3289
  }
3148
- ]
3290
+ ],
3291
+ "markdownDescription": "A long-running process started with the lane, such as a dev server or a worker. Each service gets its own port from the lane's block (`PORT`, `${{ self.port }}`); devlanes stops it with the lane and never restarts it when it exits. A service needs `run` or `run_script`, directly or through `extends`."
3149
3292
  },
3150
3293
  "default": {},
3151
- "description": "Reusable service settings, inherited with extends after local overrides are merged.",
3294
+ "description": "Named service settings that services reuse with `extends: <template>`. The service inherits every setting of the template and its own settings win: mappings (`env`, `ready`, `stop`, `fnox`, preset options, `run_script`) merge by key, lists (`tags`, `hosts`, `sync`) and single values replace. Setting `run` or `run_script` drops the inherited other one, and one `ready` probe (`tcp`, `http`, `log`, `cmd`) drops the inherited probe. Templates may extend other templates, are never started themselves, and are applied after `devlanes.local.yaml` is merged.",
3152
3295
  "examples": [
3153
3296
  {
3154
3297
  "fastify-api": {
@@ -3164,9 +3307,9 @@
3164
3307
  }
3165
3308
  }
3166
3309
  ],
3310
+ "markdownDescription": "Named service settings that services reuse with `extends: <template>`. The service inherits every setting of the template and its own settings win: mappings (`env`, `ready`, `stop`, `fnox`, preset options, `run_script`) merge by key, lists (`tags`, `hosts`, `sync`) and single values replace. Setting `run` or `run_script` drops the inherited other one, and one `ready` probe (`tcp`, `http`, `log`, `cmd`) drops the inherited probe. Templates may extend other templates, are never started themselves, and are applied after `devlanes.local.yaml` is merged.",
3167
3311
  "type": "object"
3168
3312
  }
3169
3313
  },
3170
- "title": "devlanes",
3171
3314
  "type": "object"
3172
3315
  }