Skip to content

Commit 088703a

Browse files
committed
feat(convention): enable MDS072 in slidev; fixtures, docs, README
Wire the opt-in slide-structure rule into the slidev convention so it turns on for decks, and make the rule code-fence-aware so a `---` or ::slot:: shown inside a fenced code block is not mis-parsed as slide structure. Adds the rule README, good/bad fixtures, the conventions-reference and rules-index entries, the walk-audit manifest classification, and the per-rule alloc ceiling (0 allocs/op — inert without slide markers). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012teDVC4T7vuxdaTBSBQ4nJ
1 parent 0b5db84 commit 088703a

20 files changed

Lines changed: 487 additions & 96 deletions

File tree

PLAN.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,4 +252,5 @@ footer: |
252252
| 2607082051 | 🔲 | opus | [Schema extensions: closed frontmatter and filename agreement](plan/2607082051_apm-schema-extensions.md) |
253253
| 2607082052 | 🔲 | sonnet | [SARIF output format for `mdsmith check`](plan/2607082052_check-sarif-output.md) |
254254
| 2607121915 || sonnet | [Resolve the internal/linkgraph purity-contract mismatch for wikilink resolution](plan/2607121915_arch-fix-linkgraph-wikilink-purity.md) |
255+
| 2607171900 | 🔳 | opus | [Slidev structure rule (MDS072) — validate layouts, slots, fields, and frontmatter keys per slide](plan/2607171900_slidev-structure-rule.md) |
255256
<?/catalog?>

docs/reference/conventions.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,11 @@ project's terms join rather than replace them.
165165
### `slidev`
166166

167167
Disables eight default-on rules that produce false
168-
positives on [Slidev](https://sli.dev) files. Slidev
169-
uses `---` as a slide separator: the separator is
170-
parsed as a setext underline, headings restart per
171-
slide, and layout-only slides carry no body. Pins no
172-
flavor; the parser-level `---` issue is out of scope.
168+
positives on [Slidev](https://sli.dev) files, and
169+
enables `slide-structure` (MDS072) to validate
170+
per-slide layouts, slots, fields, and keys. Slidev
171+
uses `---` as a slide separator (parsed as a setext
172+
underline); headings restart per slide. Pins no flavor.
173173

174174
| Rule | Why disabled |
175175
| --------------------------------------------- | ------------------------------------------ |

docs/research/markdownlint-coverage/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,7 @@ row-expr: |
825825
| [MDS067](../../../internal/rules/MDS067-callout-type/README.md) callout-type |||||||
826826
| [MDS069](../../../internal/rules/MDS069-unique-frontmatter/README.md) unique-frontmatter |||||||
827827
| [MDS071](../../../internal/rules/MDS071-required-frontmatter/README.md) required-frontmatter |||||||
828+
| [MDS072](../../../internal/rules/MDS072-slide-structure/README.md) slide-structure |||||||
828829
<?/catalog?>
829830

830831
## Generated sections (directives) (mdsmith-only)

internal/config/convention_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,12 @@ func TestApplyConvention_Slidev_DisablesEightRulesInPreset(t *testing.T) {
175175
assert.False(t, p.Enabled, "slidev preset must disable rule %q", rule)
176176
}
177177
}
178+
179+
// slidev also enables the opt-in slide-structure rule.
180+
ss, ok := cfg.ConventionPreset["slide-structure"]
181+
if assert.True(t, ok, "slidev preset must contain slide-structure") {
182+
assert.True(t, ss.Enabled, "slidev preset must enable slide-structure")
183+
}
178184
}
179185

180186
func TestApplyConvention_FlavorAgreeAccepted(t *testing.T) {

internal/convention/convention.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,14 @@ var conventions = map[string]Convention{
201201
// guard for a convention whose flavor is FlavorAny.
202202
//
203203
// Parser-level handling of `---` as a page separator is out of
204-
// scope. This convention silences the rule-level false positives
205-
// only.
204+
// scope for the disabled rules above.
205+
//
206+
// Beyond silencing false positives, the convention enables
207+
// slide-structure (MDS072): a Slidev-aware rule that adds checks
208+
// the built-in rules cannot express — unmatched ::slot:: markers,
209+
// unknown layouts, missing layout-required fields, and typo'd
210+
// per-slide frontmatter keys. MDS072 is opt-in (off by default),
211+
// so the convention is what turns it on for decks.
206212
"slidev": {
207213
Name: "slidev",
208214
Flavor: FlavorAny,
@@ -215,6 +221,7 @@ var conventions = map[string]Convention{
215221
"no-trailing-punctuation-in-heading": {Enabled: false},
216222
"no-emphasis-as-heading": {Enabled: false},
217223
"empty-section-body": {Enabled: false},
224+
"slide-structure": {Enabled: true},
218225
},
219226
},
220227
// no-llm-tells ships the mechanical layer of the docs-author

internal/convention/convention_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,15 @@ func TestLookup_Slidev(t *testing.T) {
266266
assert.False(t, p.Enabled, "slidev convention must disable rule %q", rule)
267267
}
268268
}
269-
require.Len(t, c.Rules, len(disabledRules),
269+
270+
// Beyond the eight disabled rules, slidev enables the opt-in
271+
// slide-structure rule (MDS072) — the additive Slidev check.
272+
ss, ok := c.Rules["slide-structure"]
273+
if assert.True(t, ok, "slidev convention must mention slide-structure") {
274+
assert.True(t, ss.Enabled, "slidev convention must enable slide-structure")
275+
}
276+
277+
require.Len(t, c.Rules, len(disabledRules)+1,
270278
"slidev convention rule count drifted")
271279
}
272280

internal/integration/perrule_bench_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ var perRuleAllocCeiling = map[string]float64{
301301
"MDS067": 12, // callout-type: ~8 allocs
302302
"MDS068": 4, // link-style: 0 allocs
303303
"MDS071": 4, // required-frontmatter: 0 allocs (inert without fields)
304+
"MDS072": 4, // slide-structure: 0 allocs (inert without slide markers)
304305
}
305306

306307
// init pins MDS043's allocs ceiling from the build-tagged

internal/integration/testdata/rule_walk_audit.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,5 +768,16 @@
768768
"is_node_checker": false,
769769
"uses_ast_walk": false,
770770
"reads_file_ast": false
771+
},
772+
{
773+
"id": "MDS072",
774+
"name": "slide-structure",
775+
"category": "A-no-skipping",
776+
"nil_ast_safe": true,
777+
"code_block_sensitive": false,
778+
"fired": true,
779+
"is_node_checker": false,
780+
"uses_ast_walk": false,
781+
"reads_file_ast": false
771782
}
772783
]

internal/rulelayer/rule_walk_audit.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,5 +768,16 @@
768768
"is_node_checker": false,
769769
"uses_ast_walk": false,
770770
"reads_file_ast": false
771+
},
772+
{
773+
"id": "MDS072",
774+
"name": "slide-structure",
775+
"category": "A-no-skipping",
776+
"nil_ast_safe": true,
777+
"code_block_sensitive": false,
778+
"fired": true,
779+
"is_node_checker": false,
780+
"uses_ast_walk": false,
781+
"reads_file_ast": false
771782
}
772783
]
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
---
2+
id: MDS072
3+
name: slide-structure
4+
status: ready
5+
description: >-
6+
Flags Slidev slide-structure errors: unknown
7+
layouts, missing or orphaned slot separators,
8+
missing layout-required fields, and misspelled
9+
per-slide frontmatter keys.
10+
nature: structure
11+
maintainability: null
12+
markdownlint: []
13+
rumdl: []
14+
mado: []
15+
panache: []
16+
obsidian-linter: []
17+
gomarklint: []
18+
category: structural
19+
---
20+
# MDS072: slide-structure
21+
22+
Flags Slidev slide-structure errors: unknown
23+
layouts, missing or orphaned slot separators,
24+
missing layout-required fields, and misspelled
25+
per-slide frontmatter keys.
26+
27+
[Slidev](https://sli.dev) renders a single Markdown
28+
file as a slide deck: `---` separates slides and
29+
each slide may carry its own frontmatter block. Its
30+
parser is permissive — an unmatched `::slot::`
31+
drops its content, a misspelled `layout:` renders
32+
blank, and an unknown frontmatter key passes
33+
through as data. None of these error. MDS072 splits
34+
the deck into slides and reports the failures
35+
Slidev never does.
36+
37+
The rule is opt-in. Select the
38+
[`slidev` convention](../../../docs/reference/conventions.md)
39+
to enable it, or turn it on directly. It owns the
40+
Markdown/structural layer only: it does not resolve
41+
theme packages, render Vue, or validate UnoCSS
42+
classes.
43+
44+
## Settings
45+
46+
| Key | Type | Description |
47+
| -------------- | ---- | ----------------------------------------------------- |
48+
| custom-layouts | list | Theme or addon layout names to treat as known layouts |
49+
50+
Declare a theme's layouts so they are not flagged
51+
as unknown:
52+
53+
```yaml
54+
rules:
55+
slide-structure:
56+
custom-layouts: [my-cover, quote-dark]
57+
```
58+
59+
## Config
60+
61+
Enable:
62+
63+
```yaml
64+
rules:
65+
slide-structure: true
66+
```
67+
68+
Disable:
69+
70+
```yaml
71+
rules:
72+
slide-structure: false
73+
```
74+
75+
## Examples
76+
77+
### Bad
78+
79+
A `two-cols` layout with no `::right::` separator —
80+
the right column renders empty.
81+
82+
<?include
83+
file: bad/missing-right-slot.md
84+
wrap: markdown
85+
?>
86+
87+
```markdown
88+
# Intro
89+
90+
Body text.
91+
92+
---
93+
layout: two-cols
94+
---
95+
96+
# Left column
97+
98+
Left body.
99+
```
100+
101+
<?/include?>
102+
103+
### Good
104+
105+
A clean deck: blank-padded separators and unique,
106+
well-formed headings.
107+
108+
<?include
109+
file: good/clean-deck.md
110+
wrap: markdown
111+
?>
112+
113+
```markdown
114+
# Opening
115+
116+
Welcome to the deck.
117+
118+
---
119+
120+
## Agenda
121+
122+
Three topics today.
123+
124+
---
125+
126+
## Summary
127+
128+
Thanks for watching.
129+
```
130+
131+
<?/include?>
132+
133+
## Meta-Information
134+
135+
- **ID**: MDS072
136+
- **Name**: `slide-structure`
137+
- **Status**: ready
138+
- **Default**: disabled
139+
- **Fixable**: no
140+
- **Implementation**:
141+
[source](./)
142+
- **Category**: structural

0 commit comments

Comments
 (0)