markform 0.1.24 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +55 -31
  2. package/dist/ai-sdk.d.mts +1 -1
  3. package/dist/ai-sdk.mjs +2 -2
  4. package/dist/bin.mjs +1 -1
  5. package/dist/{cli-B1DhFYBS.mjs → cli-BMQ9k9z7.mjs} +85 -40
  6. package/dist/cli-BMQ9k9z7.mjs.map +1 -0
  7. package/dist/cli.mjs +1 -1
  8. package/dist/{coreTypes-GxzWNXap.d.mts → coreTypes-CxpqKpBA.d.mts} +45 -2
  9. package/dist/{coreTypes-CctFK6uE.mjs → coreTypes-DIv9Aabl.mjs} +19 -5
  10. package/dist/coreTypes-DIv9Aabl.mjs.map +1 -0
  11. package/dist/{fillRecord-DeqI2pQ5.d.mts → fillRecord-V3vlyobd.d.mts} +5 -1
  12. package/dist/{fillRecordRenderer-VBQ2vwPV.mjs → fillRecordRenderer-BqRPHPmE.mjs} +47 -15
  13. package/dist/fillRecordRenderer-BqRPHPmE.mjs.map +1 -0
  14. package/dist/index.d.mts +32 -4
  15. package/dist/index.mjs +4 -4
  16. package/dist/{prompts-BCnYaH4_.mjs → prompts-4jZmkGKW.mjs} +114 -11
  17. package/dist/prompts-4jZmkGKW.mjs.map +1 -0
  18. package/dist/render.d.mts +2 -2
  19. package/dist/render.mjs +1 -1
  20. package/dist/{session-BLjN3BkJ.mjs → session-BW9jtYNV.mjs} +2 -2
  21. package/dist/{session-BLjN3BkJ.mjs.map → session-BW9jtYNV.mjs.map} +1 -1
  22. package/dist/{session-D7C7IlEv.mjs → session-DHyTMP67.mjs} +1 -1
  23. package/dist/{shared-DtorFV21.mjs → shared-BLh342F5.mjs} +1 -1
  24. package/dist/{shared-CuSRYcIB.mjs → shared-BszoSkAO.mjs} +8 -8
  25. package/dist/{shared-CuSRYcIB.mjs.map → shared-BszoSkAO.mjs.map} +1 -1
  26. package/dist/{src-C5OWf1dL.mjs → src-Dy3cDjDS.mjs} +165 -35
  27. package/dist/src-Dy3cDjDS.mjs.map +1 -0
  28. package/docs/markform-apis.md +19 -7
  29. package/docs/markform-reference.md +247 -178
  30. package/docs/markform-spec.md +81 -33
  31. package/docs/skill/SKILL.md +62 -20
  32. package/examples/markform-demo-playbook.md +342 -0
  33. package/examples/parallel/parallel-research.form.md +2 -6
  34. package/examples/simple/simple-mock-filled.report.md +2 -2
  35. package/examples/simple/simple-skipped-filled.report.md +2 -2
  36. package/examples/twitter-thread/twitter-thread.form.md +5 -5
  37. package/package.json +1 -1
  38. package/dist/cli-B1DhFYBS.mjs.map +0 -1
  39. package/dist/coreTypes-CctFK6uE.mjs.map +0 -1
  40. package/dist/fillRecordRenderer-VBQ2vwPV.mjs.map +0 -1
  41. package/dist/prompts-BCnYaH4_.mjs.map +0 -1
  42. package/dist/src-C5OWf1dL.mjs.map +0 -1
  43. package/examples/startup-research/startup-research-mock-filled.form.md +0 -297
  44. package/examples/startup-research/startup-research.form.md +0 -181
@@ -1,12 +1,12 @@
1
- <!--
2
- SPDX-License-Identifier: CC-BY-4.0
1
+ <!-- SPDX-License-Identifier: CC-BY-4.0
3
2
 
4
3
  Markform Quick Reference - Licensed under Creative Commons Attribution 4.0 International
5
4
  https://creativecommons.org/licenses/by/4.0/
6
5
 
7
6
  You may freely implement this specification in your own software under any license.
8
- The reference implementation at https://github.com/jlevy/markform is separately
9
- licensed under AGPL-3.0-or-later. Contact the author for commercial licensing options.
7
+ The reference implementation at https://github.com/jlevy/markform is separately licensed
8
+ under AGPL-3.0-or-later.
9
+ Contact the author with licensing questions or for other licensing options.
10
10
  -->
11
11
 
12
12
  # Markform Quick Reference
@@ -64,14 +64,15 @@ markform:
64
64
  ### File Extensions
65
65
 
66
66
  | File Type | Extension | Description |
67
- |-----------|-----------|-------------|
67
+ | --- | --- | --- |
68
68
  | Form | `.form.md` | Markform source and filled forms |
69
69
  | Fill Record | `.fill.json` | Execution metadata (sidecar file) |
70
70
  | Report | `.report.md` | Filtered human-readable output |
71
71
  | Schema | `.schema.json` | JSON Schema for form structure |
72
72
  | Values | `.yml` or `.json` | Exported field values |
73
73
 
74
- **Recommended:** Use `.form.md` for all Markform files. This enables:
74
+ **Recommended:** Use `.form.md` for all Markform files.
75
+ This enables:
75
76
  - Auto-discovery of fill records by `markform serve`
76
77
  - Consistent tooling behavior across CLI commands
77
78
  - Clear distinction from regular markdown files
@@ -80,10 +81,12 @@ Markform uses HTML comment syntax for structure tags, which render invisibly on
80
81
 
81
82
  ### Syntax
82
83
 
83
- **Primary syntax** uses HTML comments:
84
+ **Default:** Always use HTML comment syntax for new forms.
85
+ This renders invisibly in Markdown renderers (including GitHub) and is the recommended
86
+ style for all Markform files.
84
87
 
85
88
  | Element | Syntax | Notes |
86
- |---------|--------|-------|
89
+ | --- | --- | --- |
87
90
  | Opening tag | `<!-- form id="x" -->` | Tag name directly after `<!--` |
88
91
  | Closing tag | `<!-- /form -->` | Closing tags |
89
92
  | Self-closing | `<!-- field ... /-->` | Self-closing tags |
@@ -97,8 +100,10 @@ Markform uses HTML comment syntax for structure tags, which render invisibly on
97
100
  <!-- group id="basics" -->
98
101
  <!-- field kind="string" id="name" label="Name" --><!-- /field -->
99
102
  <!-- field kind="single_select" id="rating" label="Rating" -->
103
+
100
104
  - [ ] Good <!-- #good -->
101
105
  - [ ] Bad <!-- #bad -->
106
+
102
107
  <!-- /field -->
103
108
  <!-- /group -->
104
109
  <!-- /form -->
@@ -107,16 +112,18 @@ Markform uses HTML comment syntax for structure tags, which render invisibly on
107
112
  ### Alternative Syntax (Markdoc Tags)
108
113
 
109
114
  Markform also supports **Markdoc tag syntax** (`{% tag %}`), which is the underlying
110
- format used internally:
115
+ format used internally.
116
+ Use HTML comments unless you have a specific reason to prefer Markdoc tags.
111
117
 
112
118
  | HTML Comment | Markdoc Tag |
113
- |--------------|-------------|
119
+ | --- | --- |
114
120
  | `<!-- form id="x" -->` | `{% form id="x" %}` |
115
121
  | `<!-- /form -->` | `{% /form %}` |
116
122
  | `<!-- #id -->` | `{% #id %}` |
117
123
  | `<!-- .class -->` | `{% .class %}` |
118
124
 
119
- Both syntaxes are always supported. Files preserve their original syntax on round-trip.
125
+ Both syntaxes are always supported.
126
+ Files preserve their original syntax on round-trip.
120
127
 
121
128
  ## Field Kinds
122
129
 
@@ -129,14 +136,14 @@ representation. See the Type System section in SPEC.md for full details.
129
136
  Single-line or multi-line text.
130
137
 
131
138
  ````markdown
132
- {% field kind="string" id="name" label="Name" required=true minLength=2 maxLength=100 %}{% /field %}
139
+ <!-- field kind="string" id="name" label="Name" required=true minLength=2 maxLength=100 --><!-- /field -->
133
140
 
134
- {% field kind="string" id="bio" label="Biography" pattern="^[A-Z].*" %}
141
+ <!-- field kind="string" id="bio" label="Biography" pattern="^[A-Z].*" -->
135
142
  ```value
136
143
  Existing value here
137
144
  ````
138
- {% /field %}
139
- ````
145
+ <!-- /field -->
146
+ `````
140
147
 
141
148
  | Attribute | Type | Description |
142
149
  |-----------|------|-------------|
@@ -148,15 +155,15 @@ Existing value here
148
155
 
149
156
  Numeric values with optional constraints.
150
157
 
151
- ```markdown
152
- {% field kind="number" id="age" label="Age" required=true min=0 max=150 integer=true %}{% /field %}
158
+ ````markdown
159
+ <!-- field kind="number" id="age" label="Age" required=true min=0 max=150 integer=true --><!-- /field -->
153
160
 
154
- {% field kind="number" id="price" label="Price" min=0.01 max=999999.99 %}
161
+ <!-- field kind="number" id="price" label="Price" min=0.01 max=999999.99 -->
155
162
  ```value
156
163
  49.99
157
- ````
158
- {% /field %}
159
- ````
164
+ `````
165
+ <!-- /field -->
166
+ `````
160
167
 
161
168
  | Attribute | Type | Description |
162
169
  |-----------|------|-------------|
@@ -168,16 +175,16 @@ Numeric values with optional constraints.
168
175
 
169
176
  Array of strings, one per line.
170
177
 
171
- ```markdown
172
- {% field kind="string_list" id="tags" label="Tags" required=true minItems=1 maxItems=10 uniqueItems=true %}{% /field %}
178
+ ````markdown
179
+ <!-- field kind="string_list" id="tags" label="Tags" required=true minItems=1 maxItems=10 uniqueItems=true --><!-- /field -->
173
180
 
174
- {% field kind="string_list" id="features" label="Key Features" minItems=3 itemMinLength=10 %}
181
+ <!-- field kind="string_list" id="features" label="Key Features" minItems=3 itemMinLength=10 -->
175
182
  ```value
176
183
  Feature one description
177
184
  Feature two description
178
185
  Feature three description
179
- ````
180
- {% /field %}
186
+ `````
187
+ <!-- /field -->
181
188
  ````
182
189
 
183
190
  | Attribute | Type | Description |
@@ -193,27 +200,31 @@ Feature three description
193
200
  Choose exactly one option.
194
201
 
195
202
  ```markdown
196
- {% field kind="single_select" id="rating" label="Rating" required=true %}
197
- - [ ] Low {% #low %}
198
- - [ ] Medium {% #medium %}
199
- - [x] High {% #high %}
200
- {% /field %}
203
+ <!-- field kind="single_select" id="rating" label="Rating" required=true -->
204
+
205
+ - [ ] Low <!-- #low -->
206
+ - [ ] Medium <!-- #medium -->
207
+ - [x] High <!-- #high -->
208
+
209
+ <!-- /field -->
201
210
  ````
202
211
 
203
212
  Options use `[ ]` (unselected) or `[x]` (selected).
204
- Each option needs `{% #id %}`.
213
+ Each option needs `<!-- #id -->`.
205
214
 
206
215
  ### Multi Select
207
216
 
208
217
  Choose multiple options.
209
218
 
210
219
  ```markdown
211
- {% field kind="multi_select" id="categories" label="Categories" required=true minSelections=1 maxSelections=3 %}
212
- - [x] Frontend {% #frontend %}
213
- - [x] Backend {% #backend %}
214
- - [ ] Database {% #database %}
215
- - [ ] DevOps {% #devops %}
216
- {% /field %}
220
+ <!-- field kind="multi_select" id="categories" label="Categories" required=true minSelections=1 maxSelections=3 -->
221
+
222
+ - [x] Frontend <!-- #frontend -->
223
+ - [x] Backend <!-- #backend -->
224
+ - [ ] Database <!-- #database -->
225
+ - [ ] DevOps <!-- #devops -->
226
+
227
+ <!-- /field -->
217
228
  ```
218
229
 
219
230
  | Attribute | Type | Description |
@@ -228,13 +239,15 @@ Stateful checklists with three modes.
228
239
  **Multi Mode** (default) - 5 states for workflow tracking:
229
240
 
230
241
  ```markdown
231
- {% field kind="checkboxes" id="tasks" label="Tasks" required=true checkboxMode="multi" %}
232
- - [ ] Research {% #research %}
233
- - [x] Design {% #design %}
234
- - [/] Implementation {% #impl %}
235
- - [*] Testing {% #test %}
236
- - [-] N/A item {% #na %}
237
- {% /field %}
242
+ <!-- field kind="checkboxes" id="tasks" label="Tasks" required=true checkboxMode="multi" -->
243
+
244
+ - [ ] Research <!-- #research -->
245
+ - [x] Design <!-- #design -->
246
+ - [/] Implementation <!-- #impl -->
247
+ - [*] Testing <!-- #test -->
248
+ - [-] N/A item <!-- #na -->
249
+
250
+ <!-- /field -->
238
251
  ```
239
252
 
240
253
  | Token | State | Meaning |
@@ -248,20 +261,24 @@ Stateful checklists with three modes.
248
261
  **Simple Mode** - 2 states (GFM compatible):
249
262
 
250
263
  ```markdown
251
- {% field kind="checkboxes" id="agreements" label="Agreements" checkboxMode="simple" required=true %}
252
- - [x] I agree to terms {% #terms %}
253
- - [ ] Subscribe to newsletter {% #news %}
254
- {% /field %}
264
+ <!-- field kind="checkboxes" id="agreements" label="Agreements" checkboxMode="simple" required=true -->
265
+
266
+ - [x] I agree to terms <!-- #terms -->
267
+ - [ ] Subscribe to newsletter <!-- #news -->
268
+
269
+ <!-- /field -->
255
270
  ```
256
271
 
257
272
  **Explicit Mode** - Requires yes/no for each:
258
273
 
259
274
  ```markdown
260
- {% field kind="checkboxes" id="confirmations" label="Confirmations" checkboxMode="explicit" required=true %}
261
- - [y] Backup completed {% #backup %}
262
- - [n] Stakeholders notified {% #notify %}
263
- - [ ] Deployment ready {% #deploy %}
264
- {% /field %}
275
+ <!-- field kind="checkboxes" id="confirmations" label="Confirmations" checkboxMode="explicit" required=true -->
276
+
277
+ - [y] Backup completed <!-- #backup -->
278
+ - [n] Stakeholders notified <!-- #notify -->
279
+ - [ ] Deployment ready <!-- #deploy -->
280
+
281
+ <!-- /field -->
265
282
  ```
266
283
 
267
284
  | Token | Value | Meaning |
@@ -272,30 +289,30 @@ Stateful checklists with three modes.
272
289
 
273
290
  ### Implicit Checkboxes (Plan Documents)
274
291
 
275
- Forms designed as task lists can omit explicit field wrappers. When a form has a
276
- `{% form %}` tag but no `{% field %}` tags, checkboxes are automatically wrapped in
277
- an implicit checkboxes field.
292
+ Forms designed as task lists can omit explicit field wrappers.
293
+ When a form has a `<!-- form -->` tag but no `<!-- field -->` tags, checkboxes are
294
+ automatically wrapped in an implicit checkboxes field.
278
295
 
279
296
  ```markdown
280
297
  ---
281
298
  markform:
282
299
  spec: MF/0.1
283
300
  ---
284
- {% form id="plan" title="Project Plan" %}
301
+ <!-- form id="plan" title="Project Plan" -->
285
302
 
286
303
  ## Phase 1: Research
287
- - [ ] Literature review {% #lit_review %}
288
- - [ ] Competitive analysis {% #comp %}
304
+ - [ ] Literature review <!-- #lit_review -->
305
+ - [ ] Competitive analysis <!-- #comp -->
289
306
 
290
307
  ## Phase 2: Design
291
- - [x] Architecture doc {% #arch %}
292
- - [/] API design {% #api %}
308
+ - [x] Architecture doc <!-- #arch -->
309
+ - [/] API design <!-- #api -->
293
310
 
294
- {% /form %}
311
+ <!-- /form -->
295
312
  ```
296
313
 
297
314
  **Requirements:**
298
- - Each checkbox MUST have an ID annotation (`{% #id %}` or `<!-- #id -->`)
315
+ - Each checkbox MUST have an ID annotation (`<!-- #id -->`)
299
316
  - IDs must be unique (same rules as explicit checkboxes fields)
300
317
  - The implicit field uses ID `checkboxes` (reserved)
301
318
  - Always uses `checkboxMode="multi"` (5-state)
@@ -306,26 +323,26 @@ markform:
306
323
  Single URL with format validation.
307
324
 
308
325
  ````markdown
309
- {% field kind="url" id="website" label="Website" required=true %}{% /field %}
326
+ <!-- field kind="url" id="website" label="Website" required=true --><!-- /field -->
310
327
 
311
- {% field kind="url" id="repo" label="Repository" %}
328
+ <!-- field kind="url" id="repo" label="Repository" -->
312
329
  ```value
313
330
  https://github.com/example/repo
314
331
  ````
315
- {% /field %}
316
- ````
332
+ <!-- /field -->
333
+ `````
317
334
 
318
335
  ### URL List
319
336
 
320
337
  Array of URLs.
321
338
 
322
- ```markdown
323
- {% field kind="url_list" id="sources" label="Sources" required=true minItems=1 maxItems=10 uniqueItems=true %}
339
+ ````markdown
340
+ <!-- field kind="url_list" id="sources" label="Sources" required=true minItems=1 maxItems=10 uniqueItems=true -->
324
341
  ```value
325
342
  https://example.com/source1
326
343
  https://example.com/source2
327
- ````
328
- {% /field %}
344
+ `````
345
+ <!-- /field -->
329
346
  `````
330
347
 
331
348
  ### Date Field
@@ -333,13 +350,13 @@ https://example.com/source2
333
350
  Date value in ISO 8601 format (YYYY-MM-DD).
334
351
 
335
352
  ````markdown
336
- {% field kind="date" id="deadline" label="Deadline" required=true %}{% /field %}
353
+ <!-- field kind="date" id="deadline" label="Deadline" required=true --><!-- /field -->
337
354
 
338
- {% field kind="date" id="start_date" label="Start Date" min="2020-01-01" max="2030-12-31" %}
355
+ <!-- field kind="date" id="start_date" label="Start Date" min="2020-01-01" max="2030-12-31" -->
339
356
  ```value
340
357
  2024-06-15
341
358
  `````
342
- {% /field %}
359
+ <!-- /field -->
343
360
  `````
344
361
 
345
362
  | Attribute | Type | Description |
@@ -352,13 +369,13 @@ Date value in ISO 8601 format (YYYY-MM-DD).
352
369
  Integer year with optional constraints.
353
370
 
354
371
  ````markdown
355
- {% field kind="year" id="release_year" label="Release Year" required=true min=1888 max=2030 %}{% /field %}
372
+ <!-- field kind="year" id="release_year" label="Release Year" required=true min=1888 max=2030 --><!-- /field -->
356
373
 
357
- {% field kind="year" id="founded" label="Year Founded" %}
374
+ <!-- field kind="year" id="founded" label="Year Founded" -->
358
375
  ```value
359
376
  2015
360
377
  `````
361
- {% /field %}
378
+ <!-- /field -->
362
379
  `````
363
380
 
364
381
  | Attribute | Type | Description |
@@ -371,33 +388,35 @@ Integer year with optional constraints.
371
388
  Structured tabular data with typed columns. Uses standard markdown table syntax.
372
389
 
373
390
  ````markdown
374
- {% field kind="table" id="team" label="Team Members" required=true
391
+ <!-- field kind="table" id="team" label="Team Members" required=true
375
392
  columnIds=["name", "title", "start_date"]
376
393
  columnLabels=["Name", "Job Title", "Start Date"]
377
394
  columnTypes=["string", "string", "date"]
378
- minRows=1 maxRows=20 %}
395
+ minRows=1 maxRows=20 -->
379
396
  | Name | Job Title | Start Date |
380
397
  |------|-----------|------------|
381
398
  | Alice Smith | Engineer | 2023-01-15 |
382
399
  | Bob Jones | Designer | 2022-06-01 |
383
- {% /field %}
400
+
401
+ <!-- /field -->
384
402
  `````
385
403
 
386
404
  **Basic table (columnLabels backfilled from header row):**
387
405
 
388
406
  ```markdown
389
- {% field kind="table" id="items" label="Items"
390
- columnIds=["name", "quantity", "price"] %}
407
+ <!-- field kind="table" id="items" label="Items"
408
+ columnIds=["name", "quantity", "price"] -->
391
409
  | Name | Quantity | Price |
392
410
  |------|----------|-------|
393
- {% /field %}
411
+
412
+ <!-- /field -->
394
413
  ```
395
414
 
396
415
  | Attribute | Type | Required | Description |
397
416
  | --- | --- | --- | --- |
398
417
  | `columnIds` | string[] | Yes | Array of snake_case column identifiers |
399
418
  | `columnLabels` | string[] | No | Display labels (defaults to header row) |
400
- | `columnTypes` | string[] | No | Column types (defaults to all `string`) |
419
+ | `columnTypes` | (string \| object)[] | No | Column types with optional constraints (defaults to all `string`) |
401
420
  | `minRows` | number | No | Minimum row count (default: 0) |
402
421
  | `maxRows` | number | No | Maximum row count (default: unlimited) |
403
422
 
@@ -405,12 +424,29 @@ Structured tabular data with typed columns. Uses standard markdown table syntax.
405
424
 
406
425
  | Type | Description | Validation |
407
426
  | --- | --- | --- |
408
- | `string` | Any text value | None |
427
+ | `string` | Any text value | None (unless constraints specified) |
409
428
  | `number` | Numeric value | Integer or float |
410
429
  | `url` | URL value | Valid URL format |
411
430
  | `date` | Date value | ISO 8601 (YYYY-MM-DD) |
412
431
  | `year` | Year value | Integer (1000-9999) |
413
432
 
433
+ **Per-column constraints:** Each column type can be an object with constraints:
434
+
435
+ ```markdown
436
+ <!-- field kind="table" id="items" label="Items"
437
+ columnIds=["name", "rank", "status"]
438
+ columnTypes=[{"type": "string", "minLength": 2}, {"type": "number", "min": 1, "max": 100, "integer": true}, {"type": "string", "enum": ["active", "inactive"]}] -->
439
+ ```
440
+
441
+ | Constraint | Types | Description |
442
+ | --- | --- | --- |
443
+ | `required` | all | Cell must have a value |
444
+ | `minLength` / `maxLength` | `string` | String length bounds |
445
+ | `pattern` | `string` | Regex pattern match |
446
+ | `enum` | `string` | Allowed values (controlled vocabulary) |
447
+ | `min` / `max` | `number`, `year`, `date` | Value bounds |
448
+ | `integer` | `number` | Must be integer |
449
+
414
450
  **Sentinel values in cells:** Use `%SKIP%` or `%ABORT%` with optional reasons:
415
451
 
416
452
  ```markdown
@@ -418,6 +454,7 @@ Structured tabular data with typed columns. Uses standard markdown table syntax.
418
454
  ```
419
455
 
420
456
  **Cell escaping:** Use `\|` for literal pipe characters in cell values.
457
+ Multi-line content in cells is encoded as `<br>` in markdown table format.
421
458
 
422
459
  ## Common Attributes
423
460
 
@@ -441,8 +478,8 @@ All fields support these attributes:
441
478
  | `examples` | string[] | Example values (helps LLMs understand expected format) |
442
479
 
443
480
  ```markdown
444
- {% field kind="string" id="name" label="Name" placeholder="Enter your name" examples=["John Doe", "Jane Smith"] %}{% /field %}
445
- {% field kind="number" id="revenue" label="Revenue" placeholder="1000000" examples=["500000", "1000000"] %}{% /field %}
481
+ <!-- field kind="string" id="name" label="Name" placeholder="Enter your name" examples=["John Doe", "Jane Smith"] --><!-- /field -->
482
+ <!-- field kind="number" id="revenue" label="Revenue" placeholder="1000000" examples=["500000", "1000000"] --><!-- /field -->
446
483
  ```
447
484
 
448
485
  Note: `placeholder` and `examples` are NOT valid on chooser fields (single-select,
@@ -450,8 +487,8 @@ multi-select, checkboxes).
450
487
 
451
488
  ## Harness Configuration
452
489
 
453
- Optional harness hints can be set in YAML frontmatter under `markform.harness`.
454
- All keys must be `snake_case` and all values must be numbers.
490
+ Optional harness hints can be set in YAML frontmatter under `markform.harness`. All keys
491
+ must be `snake_case` and all values must be numbers.
455
492
  These are suggestions — a harness may ignore or override them via API options.
456
493
 
457
494
  | Key | Type | Description |
@@ -478,21 +515,21 @@ Unrecognized keys or non-numeric values cause parse errors.
478
515
  Add context to fields, groups, or the form.
479
516
 
480
517
  ```markdown
481
- {% description ref="form_id" %}
518
+ <!-- description ref="form_id" -->
482
519
  Overall form description and purpose.
483
- {% /description %}
520
+ <!-- /description -->
484
521
 
485
- {% instructions ref="field_id" %}
522
+ <!-- instructions ref="field_id" -->
486
523
  Step-by-step guidance for filling this field.
487
- {% /instructions %}
524
+ <!-- /instructions -->
488
525
 
489
- {% notes ref="field_id" %}
526
+ <!-- notes ref="field_id" -->
490
527
  Additional context or caveats.
491
- {% /notes %}
528
+ <!-- /notes -->
492
529
 
493
- {% examples ref="field_id" %}
530
+ <!-- examples ref="field_id" -->
494
531
  Example values: "AAPL", "GOOGL", "MSFT"
495
- {% /examples %}
532
+ <!-- /examples -->
496
533
  ```
497
534
 
498
535
  Place doc blocks after the element they reference.
@@ -501,7 +538,7 @@ Place doc blocks after the element they reference.
501
538
 
502
539
  - **Form/Group/Field IDs**: Globally unique, `snake_case`
503
540
 
504
- - **Option IDs**: Unique within field, `snake_case`, use `{% #id %}` syntax
541
+ - **Option IDs**: Unique within field, `snake_case`, use `<!-- #id -->` syntax
505
542
 
506
543
  - **Qualified refs**: `field_id.option_id` for external references
507
544
 
@@ -516,8 +553,8 @@ roles:
516
553
  ```
517
554
 
518
555
  ```markdown
519
- {% field kind="string" id="query" label="Search Query" role="user" %}{% /field %}
520
- {% field kind="string" id="summary" label="AI Summary" role="agent" %}{% /field %}
556
+ <!-- field kind="string" id="query" label="Search Query" role="user" --><!-- /field -->
557
+ <!-- field kind="string" id="summary" label="AI Summary" role="agent" --><!-- /field -->
521
558
  ```
522
559
 
523
560
  ## Value Encoding
@@ -525,17 +562,17 @@ roles:
525
562
  Values use fenced code blocks with language `value`:
526
563
 
527
564
  ````markdown
528
- {% field kind="string" id="name" label="Name" %}
565
+ <!-- field kind="string" id="name" label="Name" -->
529
566
  ```value
530
567
  John Smith
531
568
  ````
532
- {% /field %}
569
+ <!-- /field -->
533
570
  ````
534
571
 
535
572
  Empty fields omit the value block entirely:
536
573
 
537
574
  ```markdown
538
- {% field kind="string" id="name" label="Name" %}{% /field %}
575
+ <!-- field kind="string" id="name" label="Name" --><!-- /field -->
539
576
  ````
540
577
 
541
578
  ## Complete Example
@@ -569,141 +606,143 @@ markform:
569
606
  max_patches_per_turn: 8
570
607
  ---
571
608
 
572
- {% form id="movie_research" title="Movie Research" %}
609
+ <!-- form id="movie_research" title="Movie Research" -->
573
610
 
574
- {% description ref="movie_research" %}
611
+ <!-- description ref="movie_research" -->
575
612
  A focused research form for gathering ratings and key statistics for any film.
576
613
  Pulls from IMDB, Rotten Tomatoes, and Metacritic.
577
- {% /description %}
614
+ <!-- /description -->
578
615
 
579
- {% group id="movie_input" title="Movie Identification" %}
616
+ <!-- group id="movie_input" title="Movie Identification" -->
580
617
 
581
- {% field kind="string" id="movie" label="Movie" role="user" required=true minLength=1 maxLength=300 %}{% /field %}
618
+ <!-- field kind="string" id="movie" label="Movie" role="user" required=true minLength=1 maxLength=300 --><!-- /field -->
582
619
 
583
- {% instructions ref="movie" %}
620
+ <!-- instructions ref="movie" -->
584
621
  Enter the movie title (add any details to help identify, like "Barbie 2023" or "the Batman movie with Robert Pattinson")
585
- {% /instructions %}
622
+ <!-- /instructions -->
586
623
 
587
- {% /group %}
624
+ <!-- /group -->
588
625
 
589
- {% group id="title_identification" title="Title Identification" %}
626
+ <!-- group id="title_identification" title="Title Identification" -->
590
627
 
591
- {% field kind="string" id="full_title" label="Full Title" role="agent" required=true %}{% /field %}
628
+ <!-- field kind="string" id="full_title" label="Full Title" role="agent" required=true --><!-- /field -->
592
629
 
593
- {% instructions ref="full_title" %}
630
+ <!-- instructions ref="full_title" -->
594
631
  Look up what film the user had in mind and fill in the official title including subtitle if any (e.g., "The Lord of the Rings: The Fellowship of the Ring").
595
- {% /instructions %}
632
+ <!-- /instructions -->
596
633
 
597
- {% /group %}
634
+ <!-- /group -->
598
635
 
599
- {% group id="sources" title="Sources" %}
636
+ <!-- group id="sources" title="Sources" -->
600
637
 
601
- {% field kind="url" id="imdb_url" label="IMDB URL" role="agent" required=true %}{% /field %}
638
+ <!-- field kind="url" id="imdb_url" label="IMDB URL" role="agent" required=true --><!-- /field -->
602
639
 
603
- {% instructions ref="imdb_url" %}
640
+ <!-- instructions ref="imdb_url" -->
604
641
  Direct link to the movie's IMDB page (e.g., https://www.imdb.com/title/tt0111161/).
605
- {% /instructions %}
642
+ <!-- /instructions -->
606
643
 
607
- {% field kind="url" id="rt_url" label="Rotten Tomatoes URL" role="agent" %}{% /field %}
644
+ <!-- field kind="url" id="rt_url" label="Rotten Tomatoes URL" role="agent" --><!-- /field -->
608
645
 
609
- {% instructions ref="rt_url" %}
646
+ <!-- instructions ref="rt_url" -->
610
647
  Direct link to the movie's Rotten Tomatoes page.
611
- {% /instructions %}
648
+ <!-- /instructions -->
612
649
 
613
- {% field kind="url" id="metacritic_url" label="Metacritic URL" role="agent" %}{% /field %}
650
+ <!-- field kind="url" id="metacritic_url" label="Metacritic URL" role="agent" --><!-- /field -->
614
651
 
615
- {% instructions ref="metacritic_url" %}
652
+ <!-- instructions ref="metacritic_url" -->
616
653
  Direct link to the movie's Metacritic page.
617
- {% /instructions %}
654
+ <!-- /instructions -->
618
655
 
619
- {% /group %}
656
+ <!-- /group -->
620
657
 
621
- {% group id="basic_details" title="Basic Details" %}
658
+ <!-- group id="basic_details" title="Basic Details" -->
622
659
 
623
- {% field kind="number" id="year" label="Release Year" role="agent" required=true min=1888 max=2030 %}{% /field %}
660
+ <!-- field kind="number" id="year" label="Release Year" role="agent" required=true min=1888 max=2030 --><!-- /field -->
624
661
 
625
- {% field kind="string_list" id="directors" label="Director(s)" role="agent" required=true %}{% /field %}
662
+ <!-- field kind="string_list" id="directors" label="Director(s)" role="agent" required=true --><!-- /field -->
626
663
 
627
- {% instructions ref="directors" %}
664
+ <!-- instructions ref="directors" -->
628
665
  One director per line. Most films have one; some have two or more co-directors.
629
- {% /instructions %}
666
+ <!-- /instructions -->
630
667
 
631
- {% field kind="number" id="runtime_minutes" label="Runtime (minutes)" role="agent" min=1 max=1000 %}{% /field %}
668
+ <!-- field kind="number" id="runtime_minutes" label="Runtime (minutes)" role="agent" min=1 max=1000 --><!-- /field -->
632
669
 
633
- {% field kind="single_select" id="mpaa_rating" label="MPAA Rating" role="agent" %}
634
- - [ ] G {% #g %}
635
- - [ ] PG {% #pg %}
636
- - [ ] PG-13 {% #pg_13 %}
637
- - [ ] R {% #r %}
638
- - [ ] NC-17 {% #nc_17 %}
639
- - [ ] NR/Unrated {% #nr %}
640
- {% /field %}
670
+ <!-- field kind="single_select" id="mpaa_rating" label="MPAA Rating" role="agent" -->
641
671
 
642
- {% /group %}
672
+ - [ ] G <!-- #g -->
673
+ - [ ] PG <!-- #pg -->
674
+ - [ ] PG-13 <!-- #pg_13 -->
675
+ - [ ] R <!-- #r -->
676
+ - [ ] NC-17 <!-- #nc_17 -->
677
+ - [ ] NR/Unrated <!-- #nr -->
678
+
679
+ <!-- /field -->
680
+
681
+ <!-- /group -->
643
682
 
644
- {% group id="imdb_ratings" title="IMDB Ratings" %}
683
+ <!-- group id="imdb_ratings" title="IMDB Ratings" -->
645
684
 
646
- {% field kind="number" id="imdb_rating" label="IMDB Rating" role="agent" min=1.0 max=10.0 %}{% /field %}
685
+ <!-- field kind="number" id="imdb_rating" label="IMDB Rating" role="agent" min=1.0 max=10.0 --><!-- /field -->
647
686
 
648
- {% instructions ref="imdb_rating" %}
687
+ <!-- instructions ref="imdb_rating" -->
649
688
  IMDB user rating (1.0-10.0 scale).
650
- {% /instructions %}
689
+ <!-- /instructions -->
651
690
 
652
- {% field kind="number" id="imdb_votes" label="IMDB Vote Count" role="agent" min=0 %}{% /field %}
691
+ <!-- field kind="number" id="imdb_votes" label="IMDB Vote Count" role="agent" min=0 --><!-- /field -->
653
692
 
654
- {% instructions ref="imdb_votes" %}
693
+ <!-- instructions ref="imdb_votes" -->
655
694
  Number of IMDB user votes (e.g., 2800000 for a popular film).
656
- {% /instructions %}
695
+ <!-- /instructions -->
657
696
 
658
- {% /group %}
697
+ <!-- /group -->
659
698
 
660
- {% group id="rotten_tomatoes_ratings" title="Rotten Tomatoes Ratings" %}
699
+ <!-- group id="rotten_tomatoes_ratings" title="Rotten Tomatoes Ratings" -->
661
700
 
662
- {% field kind="number" id="rt_critics_score" label="Tomatometer (Critics)" role="agent" min=0 max=100 %}{% /field %}
701
+ <!-- field kind="number" id="rt_critics_score" label="Tomatometer (Critics)" role="agent" min=0 max=100 --><!-- /field -->
663
702
 
664
- {% instructions ref="rt_critics_score" %}
703
+ <!-- instructions ref="rt_critics_score" -->
665
704
  Tomatometer percentage (0-100).
666
- {% /instructions %}
705
+ <!-- /instructions -->
667
706
 
668
- {% field kind="number" id="rt_critics_count" label="Critics Review Count" role="agent" min=0 %}{% /field %}
707
+ <!-- field kind="number" id="rt_critics_count" label="Critics Review Count" role="agent" min=0 --><!-- /field -->
669
708
 
670
- {% field kind="number" id="rt_audience_score" label="Audience Score" role="agent" min=0 max=100 %}{% /field %}
709
+ <!-- field kind="number" id="rt_audience_score" label="Audience Score" role="agent" min=0 max=100 --><!-- /field -->
671
710
 
672
- {% instructions ref="rt_audience_score" %}
711
+ <!-- instructions ref="rt_audience_score" -->
673
712
  Audience Score percentage (0-100).
674
- {% /instructions %}
713
+ <!-- /instructions -->
675
714
 
676
- {% /group %}
715
+ <!-- /group -->
677
716
 
678
- {% group id="metacritic_ratings" title="Metacritic Ratings" %}
717
+ <!-- group id="metacritic_ratings" title="Metacritic Ratings" -->
679
718
 
680
- {% field kind="number" id="metacritic_score" label="Metacritic Score" role="agent" min=0 max=100 %}{% /field %}
719
+ <!-- field kind="number" id="metacritic_score" label="Metacritic Score" role="agent" min=0 max=100 --><!-- /field -->
681
720
 
682
- {% instructions ref="metacritic_score" %}
721
+ <!-- instructions ref="metacritic_score" -->
683
722
  Metascore (0-100 scale). Leave empty if not available.
684
- {% /instructions %}
723
+ <!-- /instructions -->
685
724
 
686
- {% /group %}
725
+ <!-- /group -->
687
726
 
688
- {% group id="summary" title="Summary" %}
727
+ <!-- group id="summary" title="Summary" -->
689
728
 
690
- {% field kind="string" id="logline" label="One-Line Summary" role="agent" maxLength=300 %}{% /field %}
729
+ <!-- field kind="string" id="logline" label="One-Line Summary" role="agent" maxLength=300 --><!-- /field -->
691
730
 
692
- {% instructions ref="logline" %}
731
+ <!-- instructions ref="logline" -->
693
732
  Brief plot summary in 1-2 sentences, no spoilers.
694
- {% /instructions %}
733
+ <!-- /instructions -->
695
734
 
696
- {% field kind="string_list" id="notable_awards" label="Notable Awards" role="agent" %}{% /field %}
735
+ <!-- field kind="string_list" id="notable_awards" label="Notable Awards" role="agent" --><!-- /field -->
697
736
 
698
- {% instructions ref="notable_awards" %}
737
+ <!-- instructions ref="notable_awards" -->
699
738
  Major awards won. One per line.
700
739
  Format: Award | Category | Year
701
740
  Example: "Oscar | Best Picture | 1995"
702
- {% /instructions %}
741
+ <!-- /instructions -->
703
742
 
704
- {% /group %}
743
+ <!-- /group -->
705
744
 
706
- {% /form %}
745
+ <!-- /form -->
707
746
  ```
708
747
 
709
748
  ## CLI Quick Reference
@@ -737,9 +776,10 @@ markform fill form.md --roles=user --interactive # Only fill user-role fields
737
776
  markform fill form.md --model anthropic/claude-sonnet-4-5 # AI fills agent fields
738
777
 
739
778
  # Export data
740
- markform export form.md --format=json # Export values as JSON
741
- markform export form.md --format=yaml # Export values as YAML
742
- markform export form.md --format=markdown # Readable markdown (strips tags)
779
+ markform export form.md --format=json # Export values as JSON
780
+ markform export form.md --format=yaml # Export values as YAML
781
+ markform export form.md --format=markdown # Full rendered markdown (includes instructions)
782
+ markform report form.md # Clean report markdown (values only, no instructions)
743
783
 
744
784
  # Export form structure as JSON Schema
745
785
  markform schema form.md # Full schema with x-markform extensions
@@ -784,6 +824,35 @@ markform fill template.form.md --mock --mock-source filled.form.md
784
824
 
785
825
  5. Use `markform inspect` to verify progress and completion
786
826
 
827
+ ## Form Design Guide
828
+
829
+ When designing a form, match each piece of data to the most specific field kind:
830
+
831
+ | Data | Field Kind | When to Use |
832
+ | --- | --- | --- |
833
+ | Free text | `string` | Names, descriptions, summaries |
834
+ | Numeric values | `number` | Financial figures, counts, scores |
835
+ | Year values | `year` | Founding year, release year |
836
+ | Calendar dates | `date` | Deadlines, event dates (YYYY-MM-DD) |
837
+ | Single URL | `url` | Website, profile page |
838
+ | Multiple URLs | `url_list` | Sources, references |
839
+ | List of text items | `string_list` | Tags, competitors, skills |
840
+ | One-of-many choice | `single_select` | Category, status, rating level |
841
+ | Multiple choices | `multi_select` | Features, capabilities, sectors |
842
+ | Structured rows | `table` | Team members, line items, history |
843
+ | Verification items | `checkboxes` | Task lists, checklists, approvals |
844
+
845
+ **Tips:**
846
+
847
+ - Use `required=true` for fields essential to the form’s purpose
848
+ - Add `pattern` for structured strings (tickers, IDs, codes)
849
+ - Use `integer=true` on number fields for counts
850
+ - Set `min`/`max` bounds on numbers and dates when valid ranges are important
851
+ - Use `checkboxMode="explicit"` when every item needs a yes/no answer
852
+ - Add `role="user"` for human-provided inputs, `role="agent"` for AI-researched data
853
+ - Include `<!-- instructions -->` blocks to guide agents on format and sources
854
+ - Organize related fields into `<!-- group -->` blocks
855
+
787
856
  ## Best Practices
788
857
 
789
858
  1. **Use descriptive IDs**: `company_revenue_m` not `rev` or `field1`
@@ -796,7 +865,7 @@ markform fill template.form.md --mock --mock-source filled.form.md
796
865
 
797
866
  5. **Assign roles**: Separate user input from agent research
798
867
 
799
- 6. **Document thoroughly**: Use `{% instructions %}` for complex fields
868
+ 6. **Document thoroughly**: Use `<!-- instructions -->` for complex fields
800
869
 
801
870
  ## Claude Code Skill Setup
802
871