| id | 163 |
|---|---|
| title | Extract mdsmith Markdown parse/produce as a public Go library |
| status | ✅ |
| summary | Expose mdsmith's Markdown parsing and a canonical producer as a stable public Go package so the linter core and release tooling share one implementation instead of separate goldmark configs. |
| model | |
| depends-on |
Give mdsmith one importable Markdown surface. It covers parse and produce. Callers stop reinventing it. They stop coupling to the linter core.
This is option C deferred from PR #291. The sync-docs work there needed to parse a doc and emit Markdown. The three options were:
- Raw goldmark inside internal/release (chosen, interim).
- Reuse the linter parser — rejected: it couples release tooling to the linter core and the architecture hub keeps that boundary.
- A public library — best end state, but a separate initiative, captured here.
Two facts make C real work, not a move:
- There is no producer to expose.
mdsmith fixis edit-based: it applies per-rule segment edits, not an AST → Markdown render. A reusable producer is net-new design. - Everything sits under internal/. A public package is a semver and documentation commitment, and the parser config in internal/rules/markdownflavor imports internal/lint, so the parse path must be untangled from linter concerns first.
When this lands, the public package replaces the interim goldmark config in internal/release/syncdocs.go. Behavior does not change.
- Design the public API: a parse
entry that returns the goldmark AST
plus front matter, and a producer entry
that surgically edits source spans (the
model sync-docs already relies on).
Decide the package path
(
pkg/markdownvs a top-level module). Decided:pkg/markdowninside the existing module (github.com/jeduden/mdsmith/pkg/markdown). Surface:Parse(source) *Document(front matter + body + AST),Splice(body, []Edit) []byte(the edit-based producer),ParseContext/NewParser/StripFrontMatter/CountLines, and theProcessingInstructionnode primitive.internal/lintre-exports these via type aliases and forwards so the single goldmark config lives only inpkg/markdown. - Untangle the parser config from internal/lint so parsing has no rule/config/diagnostic dependency.
- Build the producer: a documented,
tested surgical-splice + render path
that does not fight
mdsmith fixoutput. - Migrate internal/release off its local goldmark config onto the public package; assert identical sync-docs output.
- Migrate the linter core to the same package so there is a single parser config in the tree.
- Add API docs and a semver policy entry under docs/development.
- A public package exposes parse + produce with a documented, stable API.
- No public parse path imports the linter core (internal/lint).
- sync-docs and the linter both consume the public package; only one goldmark config remains in the tree.
- sync-docs output is byte-identical before and after the migration.
- All tests pass:
go test ./.... -
go tool golangci-lint runreports no issues. -
mdsmith check .passes.