| id | 101 |
|---|---|
| title | build directive and MDS039 lint rule |
| status | ✅ |
| summary | Add the `<?build?>` directive and MDS039, which validates directive params against the recipe's declared schema and keeps the body in sync with the rendered `body-template` on `mdsmith fix`. No external tool runs at lint time. |
| model | sonnet |
Let authors declare a build artifact with a
<?build?> directive. mdsmith fix renders the
body from the recipe's body-template. MDS039
validates params and reports generated section is out of date
when the rendered body diverges from
the actual body — without running any external tool.
Depends on plan 100 (build: config block and
MDS040). Plan 102 adds the actual build execution.
After this plan, mdsmith check and mdsmith fix
work end-to-end for <?build?> blocks; artifact
files are not yet produced.
All recipes must be declared in build.recipes in
.mdsmith.yml. There are no built-in recipes.
<?build
recipe: render
source: diagram.svg
output: docs/diagram.png
?>

<?/build?>
The directive uses the same block form as
<?catalog?> and <?include?>. Inline form is
not supported.
Common parameters (all recipes):
| Name | Required | Description |
|---|---|---|
recipe |
yes | Recipe name declared in build.recipes |
output |
yes | Artifact path relative to the Markdown file; no .. components |
output accepts any file extension; MDS039 applies
no extension filter.
Each recipe has a body-template rendered by
mdsmith fix:
| Placeholder | Value |
|---|---|
{output} |
The output param value |
{alt} |
"{recipe} output: {output}" (default) |
When body-template is omitted from the recipe
declaration, the default [{output}]({output})
is used.
User-declared recipes may set body-template
in build.recipes.NAME.body-template (plan 100).
- ID:
MDS039 - Name:
build - Category:
meta - Default: enabled
- Fixable: yes (body only)
Validation:
reciperesolves — the recipe name must be declared inbuild.recipes.outputis safe — relative path, no..components, inside the project root.- Required params present —
params.requiredentries from the recipe schema must all be supplied by the directive. - No unknown params — params not in the recipe's
requiredoroptionallists produce a warning. - Body in sync — the section body must equal the
rendered
body-template. MDS039 reportsgenerated section is out of datewhen it diverges;Fixrewrites the body usinggensection.Engine.
A Markdown file can only reference recipes declared
in .mdsmith.yml. It cannot introduce a new recipe.
- MDS027: a missing artifact file fires MDS027 independently; MDS039 does not duplicate it.
- MDS040: validates
build.recipescommand safety; MDS039 validates<?build?>usage in Markdown files. - merge-driver: regenerates the
<?build?>body on conflict; artifact bytes are not regenerated.
- Implement the
<?build?>directive ininternal/rules/build/usinggensection.Engine. Register as MDS039, categorymeta.Generaterendersbody-templateonly; it never calls a builder or touches the filesystem. - Implement MDS039 validation (recipe resolution,
outputpath safety, required params, unknown params, stale-body check). - Add
good/,bad/, andfixed/fixtures for MDS039 underinternal/rules/MDS039-build/. - Wire MDS039 into
cmd/mdsmith/main.go. - Document MDS039 in
internal/rules/MDS039-build/README.md. - Add user guide at
docs/guides/directives/build.mdcovering the directive syntax and howmdsmith fixkeeps the body in sync.
-
<?build?>body is regenerated onmdsmith fixusing the recipe'sbody-template - MDS039 reports
generated section is out of datewhen the body diverges from the renderedbody-template -
mdsmith checkdoes not run any external tool for<?build?>blocks - MDS039 rejects an unknown recipe name
- MDS039 rejects a missing
outputparam - MDS039 rejects an
outputvalue that contains..components - MDS039 rejects a directive that omits a required param declared by the recipe
- MDS039 warns on a param not in the recipe's
requiredoroptionallists - A Markdown file cannot introduce a new recipe;
it can only reference recipes in
.mdsmith.yml -
outputaccepts any file extension; no extension filter is applied - The rendered
body-templateuses{alt}defaulting to"{recipe} output: {output}" - A user-declared recipe's
body-templatefrombuild.recipesis used instead of the default - Merge driver regenerates
<?build?>bodies on conflict (viagensection.Engine) - All tests pass:
go test ./... -
go tool golangci-lint runreports no issues