Skip to content

📖 [Docs]: Markdown object model specified as a section tree - #33

Merged
Marius Storhaug (MariusStorhaug) merged 5 commits into
mainfrom
docs-32-markdown-object-model-spec
Aug 2, 2026
Merged

📖 [Docs]: Markdown object model specified as a section tree#33
Marius Storhaug (MariusStorhaug) merged 5 commits into
mainfrom
docs-32-markdown-object-model-spec

Conversation

@MariusStorhaug

@MariusStorhaug Marius Storhaug (MariusStorhaug) commented Aug 2, 2026

Copy link
Copy Markdown
Member

The Markdown module now has a written, normative contract for the object model a Markdown document parses into — and that model is organised the way a document actually reads: a document holds a tree of sections, and a section owns its heading, its own content, and the sections nested inside it, recursively. Nothing in the module behaves differently yet. This is documentation of the intended state, published before the code exists so that anyone who will depend on the model can read it, disagree with it, and shape it while that is still free.

New: The Markdown object model is documented as a capability

docs/markdown-object-model/ holds the capability in three documents side by side — index.md introduces it and shows the shape at a glance, spec.md states why the model exists and what it must do, and design.md explains how it is built. The spec is normative and testable: thirteen functional requirements and three non-functional ones, each individually addressable, acceptance criteria written as scenarios, and CommonMark conformance measured against the specification's own published example set rather than against hand-picked samples.

New: Sections are the unit the model is built around

The spec settles the questions that decide what working with a parsed document feels like. A section owns the content that follows its heading up to the next heading of the same or a lower level. A section with no subsections is the same type as one that has them, holding an empty collection — there is no separate leaf type to branch on. The document is the same kind of container as a section, minus a heading and plus the metadata part, so content before the first heading belongs to the document and the same code walks both. A heading inside a block quote or a list item sections that container and leaves the document's outline alone. A skipped heading level nests without inventing a section that is not in the document, and every heading re-renders at its original level.

$doc = Get-Content -Raw 'README.md' | ConvertFrom-Markdown

$doc.GetSection('Usage').Descendants('Link') | Select-Object Destination, Title
$doc.GetSection('Usage', 'Parameters').Children = $generated.Children

$doc | ConvertTo-Markdown | Set-Content 'README.md'

New: What the model will not do is stated as plainly as what it will

Byte-exact round-tripping is a non-goal — the model preserves the stylistic choices a reader would notice and normalises the rest. Rendering to formats other than Markdown is a non-goal, because the model is plain data that any general-purpose serializer can already take somewhere else. Replacing the composition DSL is a non-goal: Set-Markdown* stays the way Markdown is written from nothing, and the object model is how existing Markdown is read and changed. The existing DSL keeps working unchanged, as a requirement rather than a promise.


Technical details
  • Documentation and one linter setting. No classes, parser, renderer, or tests are added, and no shipped file changes.
  • Implementation plan progress — completes both Specification tasks in the implementation plan of Model the markdown document as a nested section tree #32 (docs/markdown-object-model/spec.md and docs/markdown-object-model/index.md). design.md is added alongside them because the Documentation Model pairs a spec with a design in the same capability folder. The Model, Parser and renderer, Tests, and Documentation tasks in that plan remain open.
  • .github/linters/.markdown-lint.yml gains an MD025 entry with front_matter_title: "". markdownlint's default pattern counts a front matter title as a top-level heading, so a document could not carry both the title mkdocs navigation reads and the H1 GitHub renders. The setting is tightly coupled to these documents — the capability folder cannot exist without it — so it lands here rather than separately. The identical gap in the inherited baseline is tracked as Allow a body heading alongside the front matter title in the markdown linter config Template-PSModule#38.
  • The three documents were authored against Model the markdown document as a nested section tree #32 and changed only where the repository's own configuration in .github/linters/ required it: the requirement anchors were tightened from { #fr1 } to {#fr1}, which is the form MD051 recognises and Python-Markdown's attr_list still accepts, and markdown was capitalised in prose to match the Markdown terminology term in .textlintrc and the existing README. The Specification template these documents follow still shows the spaced form, tracked as Use unspaced requirement anchors in the specification template MSXOrg/docs#141, so these files are ahead of the standard rather than departing from it. Verified locally with markdownlint-cli2 and textlint run against those exact config files; both are clean.
  • A repository-root docs/ folder is not published to GitHub Pages today: the site is assembled from generated function help, src/en-US about topics, README.md, and the icon, and Structure-Site/src/main.ps1 copies nothing else. These pages are therefore read in the repository, and the {#fr1} anchors stay inert until mkdocs renders them. Tracked as Publish hand-authored documentation from a repository's docs folder Process-PSModule#437.
  • This repository was missing the NoRelease label entirely, even though .github/release.yml already excludes NoRelease from generated release notes — so that exclusion could never fire, and every documentation and maintenance pull request has been landing under "Other Changes". A sweep of the organization found 58 repositories referencing NoRelease in release.yml and this one as the only one without the label. The label now exists and is applied here alongside documentation, which makes both correct. The change also touches no important file pattern (^src/, ^README\.md$), so no release is produced either way.
Changed surface Standards checked Framework docs checked Result
docs/** (Markdown) .github/linters/.markdown-lint.yml, .github/linters/.textlintrc Documentation Model, Spec-Driven Development Aligned
.github/linters/** markdownlint rule configuration Repository Standard, Template-PSModule baseline Fixed in this PR — inherited baseline tracked as PSModule/Template-PSModule#38
Relevant issues (or links)

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Drop the front matter title so the H1 is the only document title, tighten the requirement anchors to the form markdownlint recognises, and capitalise Markdown in prose.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

No Significant Changes Detected

This PR does not contain changes to files that would trigger a new release:

Path Description
src/** Module source code
README.md Documentation

Build, test, and publish stages will be skipped for this PR.

If you believe this is incorrect, please verify that your changes are in the correct locations.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Super-linter summary

Language Validation result
CHECKOV Pass ✅
GITHUB_ACTIONS Pass ✅
GITLEAKS Pass ✅
GIT_MERGE_CONFLICT_MARKERS Pass ✅
MARKDOWN Pass ✅
NATURAL_LANGUAGE Pass ✅
POWERSHELL Pass ✅
PRE_COMMIT Pass ✅
SPELL_CODESPELL Pass ✅
TRIVY Pass ✅
YAML Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

markdownlint's default MD025 pattern counts a front matter title as a top-level heading, so a capability document could not carry both. Configure front_matter_title as empty and restore the title the mkdocs navigation reads.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

No Significant Changes Detected

This PR does not contain changes to files that would trigger a new release:

Path Description
src/** Module source code
README.md Documentation

Build, test, and publish stages will be skipped for this PR.

If you believe this is incorrect, please verify that your changes are in the correct locations.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Super-linter summary

Language Validation result
CHECKOV Pass ✅
GITHUB_ACTIONS Pass ✅
GITLEAKS Pass ✅
GIT_MERGE_CONFLICT_MARKERS Pass ✅
MARKDOWN Pass ✅
NATURAL_LANGUAGE Pass ✅
POWERSHELL Pass ✅
PRE_COMMIT Pass ✅
SPELL_CODESPELL Pass ✅
TRIVY Pass ✅
YAML Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

@MariusStorhaug Marius Storhaug (MariusStorhaug) added the NoRelease Excluded from release notes; no version bump label Aug 2, 2026
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

No Significant Changes Detected

This PR does not contain changes to files that would trigger a new release:

Path Description
src/** Module source code
README.md Documentation

Build, test, and publish stages will be skipped for this PR.

If you believe this is incorrect, please verify that your changes are in the correct locations.

@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

Super-linter summary

Language Validation result
CHECKOV Pass ✅
GITHUB_ACTIONS Pass ✅
GITLEAKS Pass ✅
GIT_MERGE_CONFLICT_MARKERS Pass ✅
MARKDOWN Pass ✅
NATURAL_LANGUAGE Pass ✅
POWERSHELL Pass ✅
PRE_COMMIT Pass ✅
SPELL_CODESPELL Pass ✅
TRIVY Pass ✅
YAML Pass ✅

All files and directories linted successfully

For more information, see the GitHub Actions workflow run

Powered by Super-linter

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Documentation changes NoRelease Excluded from release notes; no version bump

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant