markdown-schema 0.2.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.
@@ -0,0 +1,282 @@
1
+ ---
2
+ name: markdown-schema
3
+ description: Author, fill, or validate `*.template.md` files. Use when the user wants to design a new template, complete (fill) a template with real content, validate a filled document against its template using the `validate-md` CLI, or debug a `validate-md` error message. Covers all three workflows for the `markdown-schema` package.
4
+ ---
5
+
6
+ # Skill: markdown-schema
7
+
8
+ Read `markdown-schema.guideline.md` next to this SKILL.md. It is the source
9
+ of truth for the directive grammar — `enum:`, field, `row`, `section`, `guide`,
10
+ and how they nest inside `<!-- TEMPLATE-ONLY: -->` comments.
11
+
12
+ This skill covers three workflows. Pick the section matching the user's task.
13
+
14
+ ## Trigger
15
+
16
+ Use this skill when the user asks to:
17
+
18
+ - **Author** a template — design, create, or modify a `*.template.md` file;
19
+ add or split sections/columns; choose a directive kind; write a
20
+ cross-section `*.refine.ts` rule.
21
+ - **Fill** a template — replace `<!-- TEMPLATE-ONLY: -->` directives with
22
+ real content; complete a partially filled document; debug why
23
+ `validate-md` rejects a filled instance.
24
+ - **Validate** a document — run `validate-md`; interpret or debug its error
25
+ output; check whether a filled document satisfies the template.
26
+
27
+ ---
28
+
29
+ ## A. Authoring a template
30
+
31
+ ### Core principles
32
+
33
+ **Constants outside, variables inside.** Apply the "survives filling" test
34
+ to every span of text: if it appears character-for-character in every correctly
35
+ filled instance, it's a constant (leave as plain markdown). Otherwise it's a
36
+ variable (wrap in `<!-- TEMPLATE-ONLY: -->`).
37
+
38
+ **Enum-placeholder anti-pattern.** A list of values like `A | B | C` looks
39
+ like scaffolding but is actually a placeholder — the author picks one and
40
+ deletes the rest. It MUST be inside an `enum:` directive, not outside.
41
+
42
+ ### Decision table: which directive to use
43
+
44
+ | Situation | Directive |
45
+ | ----------------------------------------------- | ------------------------------------------- |
46
+ | Author picks exactly one from a fixed list | `enum: A \| B \| C; required` |
47
+ | Author writes a free-form string (required) | `string; required` |
48
+ | Author writes a free-form string (optional) | `string; optional` |
49
+ | Value must match a regex pattern | `string; regex \`^...$\`; required` |
50
+ | Value is only valid when another field equals X | `...; only-if FieldName=X` |
51
+ | Author-facing guidance (ignored by parser) | `guide` block with `//`-prefixed lines |
52
+ | Table where every row must conform to a schema | `row; min-rows: N` block directive |
53
+ | Section or table may be absent | `<!-- TEMPLATE-ONLY: section; optional -->` |
54
+ | Section must be absent when a condition holds | `section; remove-if FieldName=Value` |
55
+ | Section has sub-groups; need ≥ N groups | `section; min-groups: N` |
56
+
57
+ ### Boundary: what can't go in a directive
58
+
59
+ These invariants **cannot** be expressed as directives and belong in `*.refine.ts`:
60
+
61
+ - Set membership across two sections (e.g. event names in one section must
62
+ appear in another section's table)
63
+ - Conditional requirements spanning more than one section
64
+ - Arithmetic on counts or values
65
+ - Pattern matching across free-form prose
66
+
67
+ ### Writing a `*.refine.ts` companion
68
+
69
+ Rules:
70
+
71
+ - Filename: `<stem>.refine.ts` (sibling to `<stem>.template.md`)
72
+ - Export: named `refine` function
73
+ - Signature: `(doc: any, ctx: z.core.$RefinementCtx) => void`
74
+ - Use `ctx.addIssue({ code: "custom", path: [...], message: "..." })` for each violation
75
+
76
+ Working stub:
77
+
78
+ ```ts
79
+ import type { z } from "zod";
80
+
81
+ export const refine = (doc: any, ctx: z.core.$RefinementCtx): void => {
82
+ // Rule: ...
83
+ };
84
+ ```
85
+
86
+ **Document rules in `guide` blocks.** Every cross-section rule enforced in
87
+ `*.refine.ts` MUST have a matching `// ...` line in a `guide` block at the
88
+ top of the template. Every `//` rule in the opening `guide` block MUST be
89
+ enforced by code in `*.refine.ts`.
90
+
91
+ ### Round-trip validation
92
+
93
+ After modifying a template:
94
+
95
+ 1. Fill the template (use the **Fill** workflow below or do it manually).
96
+ 2. Write at least one **negative fixture** per refine rule (a doc that violates the rule).
97
+ 3. Run `validate-md --template <template> <doc>` on both happy and sad paths.
98
+ 4. Confirm the error messages are actionable and point to the right path.
99
+
100
+ ### Adding a column to an existing table
101
+
102
+ 1. Add the column to the header row and separator row.
103
+ 2. Add the column spec to the `row` directive block.
104
+ 3. Add sample data for the column to the sample row.
105
+ 4. Update all existing fixture files to include the new column.
106
+ 5. Re-run `validate-md` on all example docs.
107
+
108
+ ### Adding a new section
109
+
110
+ 1. Add the H2 heading with the appropriate numbering.
111
+ 2. Add a `<!-- TEMPLATE-ONLY: section -->` directive if the section is optional
112
+ or has group constraints.
113
+ 3. Add the body: table with `row` directive, labeled list with `field`/`enum:`
114
+ directives, or prose placeholder.
115
+ 4. Update `headingToKey` mappings if needed (the key is auto-derived from the
116
+ heading text via camelCase).
117
+ 5. If the section participates in cross-section invariants, update `*.refine.ts`
118
+ and the opening `guide` block at the top of the template.
119
+
120
+ ---
121
+
122
+ ## B. Filling a template
123
+
124
+ ### 1. Pre-flight
125
+
126
+ Before writing anything:
127
+
128
+ 1. Read the `*.template.md` to understand the full schema.
129
+ 2. Read the sibling `*.refine.ts` (if present) to understand cross-section invariants.
130
+ 3. Read the opening `<!-- TEMPLATE-ONLY: guide -->` block at the top of the template
131
+ for the prose list of cross-section rules.
132
+
133
+ ### 2. Fill each section
134
+
135
+ Work section by section, applying these rules per directive kind:
136
+
137
+ **`enum:` directive:**
138
+
139
+ - Write exactly one of the listed choices after the label colon.
140
+ - Delete the entire `<!-- TEMPLATE-ONLY: ... -->` inline block.
141
+ - Example: `- Type: View` (not `- Type: Root | Layout | View | ...`)
142
+
143
+ **Field directive (`string`):**
144
+
145
+ - Write the value after the label colon.
146
+ - Delete the `<!-- TEMPLATE-ONLY: ... -->` inline block.
147
+ - If `optional` and not applicable, either omit the value (leave blank after colon) or remove the line.
148
+ - Respect `only-if <Key>=<Value>`: only include the field when the named field equals the given value.
149
+
150
+ **`row` directive (table):**
151
+
152
+ - Replace the sample rows with real data rows.
153
+ - The `<!-- TEMPLATE-ONLY: row ... -->` block is in the template only; it won't appear in a correctly filled document.
154
+ - Respect `required` columns (must have a value) and `enum: A | B` columns (value must be one of the choices).
155
+ - Respect `min-rows: N` — the table needs at least N data rows.
156
+
157
+ **`section` directive:**
158
+
159
+ - If marked `optional` and not applicable: delete the entire section (heading + body).
160
+ - If `remove-if <Key>=<Value>` applies to the current doc's field value: delete the section.
161
+ - Otherwise: fill the section body according to the body shape (table, list, etc.).
162
+
163
+ **`guide` blocks:**
164
+
165
+ - These are documentation only — author-facing prose. Do not include them in the filled document.
166
+
167
+ ### 3. Cross-reference checklist
168
+
169
+ Every backtick-quoted identifier in prose sections (event names, prop names,
170
+ state keys) must resolve against the table that declares it. Check especially:
171
+
172
+ - Event names in Visual State `Visuals:` lines → declared in the Outputs table.
173
+ - Prop names in descriptions → declared in the Inputs table.
174
+
175
+ ### 4. Validate
176
+
177
+ Run: `validate-md --template <template-file> <filled-doc>`
178
+
179
+ Do not claim done until it prints `OK`. Use the **Validate** workflow below
180
+ to interpret any errors.
181
+
182
+ ### 5. Final grep
183
+
184
+ `grep '<!-- TEMPLATE-ONLY' <filled-doc>` must return empty.
185
+
186
+ ### Common fill failures
187
+
188
+ | Error signature | Cause | Fix |
189
+ | ---------------------------------------------- | ----------------------------------- | ------------------------------------------------ |
190
+ | `Invalid option: expected one of ...` | `enum:` field has wrong value | Use exactly one listed choice |
191
+ | `does not match required format` | `regex` modifier violated | Fix the value format |
192
+ | `is required` | Required field left blank | Provide a value |
193
+ | `must contain a table` | Table section is empty or absent | Add the required table |
194
+ | `Missing required section` | Required H2 heading deleted | Restore the heading |
195
+ | `Too small: N` | `min-rows` or `min-groups` violated | Add more rows/groups |
196
+ | `View components must not have internal state` | Cross-section refine | Remove the Internal State section or change Type |
197
+ | Event `onXxx` not declared | Cross-section refine | Add the event to the Outputs table |
198
+
199
+ ---
200
+
201
+ ## C. Validating a document
202
+
203
+ ### 1. Locate the template
204
+
205
+ 1. Look for a sibling `*.template.md` with the same stem as the document.
206
+ 2. If none, walk parent directories until a `*.template.md` is found.
207
+ 3. Check for a sibling `*.refine.ts` — its rules are enforced by the schema.
208
+
209
+ ### 2. Always invoke the CLI
210
+
211
+ Never reimplement parsing inline. Always use:
212
+
213
+ ```sh
214
+ validate-md --template <template-file> <doc-file>
215
+ ```
216
+
217
+ ### 3. Interpret the output
218
+
219
+ **`OK`** — document passes all schema and refine checks.
220
+
221
+ **Structural error** (single line, no path prefix):
222
+
223
+ ```
224
+ example.md: must contain a table
225
+ ```
226
+
227
+ The document is missing required structure. Fix the document shape (heading
228
+ present but body wrong), not the values.
229
+
230
+ **Zod issue list** (indented `path: message` lines):
231
+
232
+ ```
233
+ example.md: schema validation failed
234
+ - classification.Type: Invalid option: expected one of "Root"|"Layout"|...
235
+ - internalState: View components must not have internal state
236
+ ```
237
+
238
+ The document has the right structure but values violate constraints.
239
+
240
+ ### 4. Triage by error type
241
+
242
+ | Error signature | Source | Remediation |
243
+ | ------------------------------------- | --------------------------------- | ------------------------------------- |
244
+ | `Invalid option: expected one of ...` | `enum:` directive | Use exactly one listed choice |
245
+ | `does not match required format` | `regex` modifier | Fix value to match the pattern |
246
+ | `is required` | field `required` modifier | Provide a non-empty value |
247
+ | `must contain a table` | `row` directive / table extractor | Add the required GFM table |
248
+ | `Missing required section: "..."` | required H2 missing | Restore the section heading |
249
+ | `Too small: N` | `min-rows` or `min-groups` | Add more rows or groups |
250
+ | Refine message (cross-section) | `*.refine.ts` | See opening `guide` block in template |
251
+
252
+ ### 5. Refine-rule diagnostics
253
+
254
+ When an issue path doesn't map cleanly to one section (e.g. `internalState`
255
+ error mentions Type from another section), it's a refine violation:
256
+
257
+ 1. Read the opening `<!-- TEMPLATE-ONLY: guide -->` block in the template for the prose rules.
258
+ 2. Open the sibling `*.refine.ts` to find the corresponding `ctx.addIssue`.
259
+ 3. The `path` in the issue points to the section that needs to change.
260
+
261
+ ### 6. Common false alarms
262
+
263
+ - **Leftover `TEMPLATE-ONLY` blocks** — `grep '<!-- TEMPLATE-ONLY' <doc>` shows
264
+ directives not removed after filling. Delete them.
265
+ - **Heading text drift** — edited a heading after filling; compare to template.
266
+ - **Smart quotes** — `"` from copy-paste instead of `"`. Replace with straight quotes.
267
+
268
+ ### 7. Report shape
269
+
270
+ Group issues by section, map each to the directive it violates, and suggest
271
+ the minimal edit. Example:
272
+
273
+ > Section `classification` (line ~20):
274
+ >
275
+ > - `Type` must be one of `Root | Layout | Container | View | Provider | Primitive`.
276
+ > Currently: `"view"` — note lowercase. Use `View`.
277
+
278
+ ### What NOT to do
279
+
280
+ - Do not edit the template to silence a doc-level error.
281
+ - Do not disable or remove refine rules.
282
+ - Do not validate by reading the doc manually — always use the CLI.