Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ab5603c
Plan 86: flip status to in-progress
claude Apr 19, 2026
5284bd5
Plan 86: add flavor and feature enums for MDS034
claude Apr 19, 2026
b48d879
Plan 86: add cached goldmark dual parser for MDS034
claude Apr 19, 2026
c2db6cb
Plan 86: add AST-based feature detection for MDS034
claude Apr 19, 2026
decd07e
Plan 86: MDS034 markdown-flavor rule (built-in features)
claude Apr 19, 2026
5f7ea4f
Plan 86: check off tasks completed in this slice
claude Apr 19, 2026
8afc5f2
fix: address Copilot review on MDS034 detect.go
claude Apr 19, 2026
8f6ac4f
fix: address Copilot re-review on MDS034
claude Apr 19, 2026
00331b0
fix: address Copilot review round 3 on MDS034
claude Apr 20, 2026
8fec493
fix: trim all ASCII whitespace after heading attribute
claude Apr 20, 2026
be8d633
chore: regenerate rule catalog after rebase
claude Apr 20, 2026
7f0384c
fix: correct MDS034 Finding doc + test comment accuracy
claude Apr 20, 2026
6307a4e
fix: drop redundant MDS033 warn priming helper
claude Apr 20, 2026
5877d4f
Plan 86: mark acceptance criteria actually verified
claude Apr 20, 2026
15aa797
Plan 86: add SuperscriptExt custom goldmark extension
claude Apr 20, 2026
de63460
Plan 86: add SubscriptExt custom goldmark extension
claude Apr 20, 2026
d4d0dce
Plan 86: add MathBlockExt custom goldmark extension
claude Apr 20, 2026
88e9597
Plan 86: add MathInlineExt custom goldmark extension
claude Apr 21, 2026
9d7f8ad
fix(MDS034): align dual parser with lint.NewFile PI parser
claude Apr 21, 2026
003917c
fix: address Copilot round on MDS034 custom extensions
claude Apr 21, 2026
fad6165
Plan 86: add AbbreviationExt custom goldmark extension
claude Apr 21, 2026
1af4c3e
Plan 86: wire 5 custom extensions into MDS034
claude Apr 21, 2026
4b3edf5
fix: address post-wiring Copilot round on MDS034 extensions
claude Apr 21, 2026
ac4f2b9
Plan 86: add pandoc, phpextra, multimarkdown, myst, any flavors
claude Apr 21, 2026
9d8d9c4
fix: drop racy sync.Once reassignment in MDS033 helper
claude Apr 21, 2026
3752076
fix: correct extension count in MDS034 Parser() doc
claude Apr 21, 2026
718690e
Plan 86: raise MDS034 package coverage
claude Apr 21, 2026
e5af280
Plan 86: add edge-case tests to close codecov patch gap
claude Apr 21, 2026
5619f7d
Plan 86: cover defensive fallbacks in MDS034 detectors
claude Apr 21, 2026
933ba1f
Plan 86: cover the last uncovered branches in MDS034
claude Apr 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ footer: |
| 83 | 🔳 | [Security hardening batch](plan/83_security-hardening-batch.md) |
| 84 | 🔲 | [Symlink default-deny for file discovery](plan/84_symlink-default-deny.md) |
| 85 | 🔳 | [Increase test coverage to 95% by extracting shared rule helpers](plan/85_coverage-to-95-percent.md) |
| 86 | 🔲 | [Markdown flavor validation](plan/86_markdown-flavor-validation.md) |
| 86 | 🔳 | [Markdown flavor validation](plan/86_markdown-flavor-validation.md) |
| 87 | 🔲 | [Flavor validation for GitHub Alerts](plan/87_markdown-flavor-github-alerts.md) |
| 88 | ✅ | [TOC directive migration aid](plan/88_toc-directive-migration.md) |
| 89 | 🔲 | [TOC generator directive and MDS035 auto-fix](plan/89_toc-generator-directive.md) |
Expand Down
1 change: 1 addition & 0 deletions cmd/mdsmith/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
_ "github.com/jeduden/mdsmith/internal/rules/include"
_ "github.com/jeduden/mdsmith/internal/rules/linelength"
_ "github.com/jeduden/mdsmith/internal/rules/listindent"
_ "github.com/jeduden/mdsmith/internal/rules/markdownflavor"
_ "github.com/jeduden/mdsmith/internal/rules/maxfilelength"
_ "github.com/jeduden/mdsmith/internal/rules/maxsectionlength"
_ "github.com/jeduden/mdsmith/internal/rules/nobareurls"
Expand Down
33 changes: 9 additions & 24 deletions internal/integration/rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
_ "github.com/jeduden/mdsmith/internal/rules/catalog"
_ "github.com/jeduden/mdsmith/internal/rules/concisenessscoring"
_ "github.com/jeduden/mdsmith/internal/rules/crossfilereferenceintegrity"
_ "github.com/jeduden/mdsmith/internal/rules/directorystructure"
"github.com/jeduden/mdsmith/internal/rules/directorystructure"
_ "github.com/jeduden/mdsmith/internal/rules/emptysectionbody"
_ "github.com/jeduden/mdsmith/internal/rules/fencedcodelanguage"
_ "github.com/jeduden/mdsmith/internal/rules/fencedcodestyle"
Expand All @@ -32,6 +32,7 @@ import (
_ "github.com/jeduden/mdsmith/internal/rules/include"
_ "github.com/jeduden/mdsmith/internal/rules/linelength"
_ "github.com/jeduden/mdsmith/internal/rules/listindent"
_ "github.com/jeduden/mdsmith/internal/rules/markdownflavor"
_ "github.com/jeduden/mdsmith/internal/rules/maxfilelength"
_ "github.com/jeduden/mdsmith/internal/rules/maxsectionlength"
_ "github.com/jeduden/mdsmith/internal/rules/nobareurls"
Expand Down Expand Up @@ -140,9 +141,15 @@ func applySettingsToRule(
}

func TestRuleFixtures(t *testing.T) {
primeDirectoryStructureWarnOnce(t)
dirs := discoverFixtureDirs(t)

// MDS033's "no allowed patterns" warning is gated by a
// process-level sync.Once. After MDS033's own fixtures run, the
// rule is left in configured=true / allowed=[] state by the
// defaults-based cleanup. Silence the guard explicitly so later
// checkAllRules walks do not depend on warning emission order.
directorystructure.SilenceConfigWarningForTesting()

for _, dir := range dirs {
base := filepath.Base(dir)
m := ruleIDPattern.FindStringSubmatch(base)
Expand Down Expand Up @@ -388,28 +395,6 @@ func runFixSingleFile(

// --- shared helpers ---

// primeDirectoryStructureWarnOnce fires MDS033's "no allowed patterns"
// sync.Once warning up front. Without this, test cleanup in
// applySettingsToRule leaves MDS033 in a configured-but-empty state,
// so the warning would fire the first time any post-MDS033 fixture
// calls checkAllRules, producing a spurious diagnostic.
func primeDirectoryStructureWarnOnce(t *testing.T) {
t.Helper()
r := rule.ByID("MDS033")
if r == nil {
return
}
cr, ok := r.(rule.Configurable)
if !ok {
return
}
require.NoError(t, cr.ApplySettings(map[string]any{"allowed": []any{}}))
f, err := lint.NewFile("prime.md", []byte("# x\n"))
require.NoError(t, err)
_ = r.Check(f)
require.NoError(t, cr.ApplySettings(cr.DefaultSettings()))
}

func discoverFixtureDirs(t *testing.T) []string {
t.Helper()
dirs, err := filepath.Glob("../../internal/rules/MDS*-*")
Expand Down
145 changes: 145 additions & 0 deletions internal/rules/MDS034-markdown-flavor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
---
id: MDS034
name: markdown-flavor
status: ready
description: >-
Flags Markdown syntax that the declared target
flavor does not render.
---
# MDS034: markdown-flavor

Flags Markdown syntax that the declared target
flavor does not render.

- **ID**: MDS034
- **Name**: `markdown-flavor`
- **Status**: ready
- **Default**: disabled
- **Fixable**: no (fix pipeline lands in a follow-up)
- **Implementation**:
[source](./)
- **Category**: meta

## Settings

| Key | Type | Description |
|--------|--------|---------------------------------|
| flavor | string | Target flavor; see table below. |

The flavor name is case-sensitive. Supported
values:

- `commonmark` — strict CommonMark; rejects every
tracked feature.
- `gfm` — GitHub Flavored Markdown; adds tables,
task lists, strikethrough, and bare-URL
autolinks.
- `goldmark` — mdsmith-defined profile; GFM plus
heading IDs.
- `pandoc` — Pandoc's default markdown; GFM plus
footnotes, definition lists, heading IDs,
superscript, subscript, math block, and inline
math. Rejects abbreviations (non-default
extension).
- `phpextra` — PHP Markdown Extra; tables,
footnotes, definition lists, heading IDs, and
abbreviations. Rejects GFM features and math.
- `multimarkdown` — MultiMarkdown; PHP Extra plus
math block and inline math.
- `myst` — MyST (Sphinx documentation flavor);
tables, strikethrough, footnotes, definition
lists, heading IDs, math block, and inline math.
- `any` — accepts every tracked feature. Use when
the target renderer is unknown or permissive and
you want to silence flavor diagnostics without
disabling the rule.

## Config

Enable with a target flavor:

```yaml
rules:
markdown-flavor:
flavor: gfm
```

Disable (default):

```yaml
rules:
markdown-flavor: false
```

## Detected features

MDS034 tracks twelve syntax features whose
support varies across Markdown flavors.

Eleven features are detected from the goldmark AST
of a dual parse. That parse enables five built-in
extensions: table, strikethrough, task list,
footnote, and definition list. It also enables the
heading-ID attribute parser. Five custom parsers
add superscript, subscript, math block, inline
math, and abbreviations.

Bare-URL autolinks are detected separately. The
detector scans text nodes from the main parse for
URL-shaped text. It skips links, autolinks, code
spans, and code blocks.

`flavor: any` accepts every feature and is omitted
from the table below.

| Feature | commonmark | gfm | goldmark | pandoc | phpextra | multimarkdown | myst |
|--------------------|------------|-----|----------|--------|----------|---------------|------|
| tables | no | yes | yes | yes | yes | yes | yes |
| task lists | no | yes | yes | yes | no | no | no |
| strikethrough | no | yes | yes | yes | no | no | yes |
| bare-URL autolinks | no | yes | yes | yes | no | no | no |
| footnotes | no | no | no | yes | yes | yes | yes |
| definition lists | no | no | no | yes | yes | yes | yes |
| heading IDs | no | no | yes | yes | yes | yes | yes |
| superscript | no | no | no | yes | no | no | no |
| subscript | no | no | no | yes | no | no | no |
| math blocks | no | no | no | yes | no | yes | yes |
| inline math | no | no | no | yes | no | yes | yes |
| abbreviations | no | no | no | no | yes | yes | no |

## Examples

### Good

<?include
file: good/gfm.md
wrap: markdown
?>

```markdown
# Heading

Text with ~~old~~ markup and a task list:

- [x] done
- [ ] todo
```

<?/include?>

### Bad

<?include
file: bad/commonmark-table.md
wrap: markdown
?>

```markdown
# Heading

| a | b |
| - | - |
| 1 | 2 |
```

<?/include?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
settings:
flavor: commonmark
diagnostics:
- line: 3
column: 1
message: "abbreviations are not supported by commonmark"
- line: 5
column: 5
message: "abbreviations are not supported by commonmark"
---
# Heading

*[HTML]: Hyper Text Markup Language

Use HTML here.
11 changes: 11 additions & 0 deletions internal/rules/MDS034-markdown-flavor/bad/commonmark-bare-url.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
settings:
flavor: commonmark
diagnostics:
- line: 3
column: 7
message: "bare-URL autolinks are not supported by commonmark"
---
# Heading

Visit https://example.com for details.
11 changes: 11 additions & 0 deletions internal/rules/MDS034-markdown-flavor/bad/commonmark-heading-id.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
settings:
flavor: commonmark
diagnostics:
- line: 1
column: 11
message: "heading IDs are not supported by commonmark"
---
# Heading {#top}

Body text.
13 changes: 13 additions & 0 deletions internal/rules/MDS034-markdown-flavor/bad/commonmark-math-block.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
settings:
flavor: commonmark
diagnostics:
- line: 3
column: 1
message: "math blocks are not supported by commonmark"
---
# Heading

$$
a^2 + b^2 = c^2
$$
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
settings:
flavor: commonmark
diagnostics:
- line: 3
column: 5
message: "inline math is not supported by commonmark"
---
# Heading

See $x+1$ above.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
settings:
flavor: commonmark
diagnostics:
- line: 3
column: 6
message: "strikethrough is not supported by commonmark"
---
# Heading

Text ~~crossed out~~ here.
11 changes: 11 additions & 0 deletions internal/rules/MDS034-markdown-flavor/bad/commonmark-subscript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
settings:
flavor: commonmark
diagnostics:
- line: 3
column: 2
message: "subscript is not supported by commonmark"
---
# Heading

H~2~O is water.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
settings:
flavor: commonmark
diagnostics:
- line: 3
column: 7
message: "superscript is not supported by commonmark"
---
# Heading

E = mc^2^ is famous.
13 changes: 13 additions & 0 deletions internal/rules/MDS034-markdown-flavor/bad/commonmark-table.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
settings:
flavor: commonmark
diagnostics:
- line: 3
column: 1
message: "tables are not supported by commonmark"
---
# Heading

| a | b |
| - | - |
| 1 | 2 |
15 changes: 15 additions & 0 deletions internal/rules/MDS034-markdown-flavor/bad/commonmark-task-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
settings:
flavor: commonmark
diagnostics:
- line: 3
column: 3
message: "task lists are not supported by commonmark"
- line: 4
column: 3
message: "task lists are not supported by commonmark"
---
# Heading

- [x] done
- [ ] todo
12 changes: 12 additions & 0 deletions internal/rules/MDS034-markdown-flavor/bad/gfm-definition-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
settings:
flavor: gfm
diagnostics:
- line: 3
column: 1
message: "definition lists are not supported by gfm"
---
# Heading

term
: definition
16 changes: 16 additions & 0 deletions internal/rules/MDS034-markdown-flavor/bad/gfm-footnote.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
settings:
flavor: gfm
diagnostics:
- line: 3
column: 1
message: "footnotes are not supported by gfm"
- line: 5
column: 1
message: "footnotes are not supported by gfm"
---
# Heading

A paragraph.[^1]

[^1]: footnote body.
Loading
Loading