| id | 77 |
|---|---|
| title | Template composition and cycle detection |
| status | ✅ |
| summary | Allow include directives in schema files for template composition, add cycle detection for all include paths, rename template to schema. |
Part of the user-model work from
plan 73.
Addresses
#71
(cycle detection) and
#73
(Hugo comparison -- composition gap).
Also addresses
#68
(schema rename clarifies user model).
Depends on: none (independent of other plans). Update plan 74 guide after landing.
Let schema files use <?include?> to share
structure across templates. Add cycle detection
to all include resolution paths so composition
is safe. Rename the config key from template
to schema to stop Hugo users reading it as a
rendering template.
Hugo-user blind trials (plan 73) found:
- 5/5 confused by the word "template" meaning validation schema, not rendering.
- 5/5 expected composition (Hugo partials and base templates compose freely).
- Issue #71 notes there is no cycle detection for include directives today.
Allowing <?include?> in schema files solves
the composition gap but also introduces a new
cycle risk: schema A includes fragment B which
includes fragment C which includes A. Cycle
detection must land in the same PR.
During parseTemplate, process <?include?>
directives in the schema file. The included
fragment's headings are spliced into the schema
heading list at the include position. Front
matter from fragments is ignored (only the
root schema's front matter defines the CUE
constraints). <?require?> in a fragment is
merged into the root schema's constraints.
Example schema (plan/proto.md):
---
id: 'int & >=1'
title: 'string & != ""'
---
<?require
filename: "[0-9]*_*.md"
?>
# ?
## Goal
One-sentence summary.
<?include
file: common/acceptance-criteria.md
?>Where common/acceptance-criteria.md has:
## Acceptance Criteria
- [ ] All tests pass: `go test ./...`The parsed schema would have headings:
# ?, ## Goal, ## Acceptance Criteria.
Fragment files must be in the ignore: list
(they are not standalone documents).
Add a visited-file set to include resolution. Applies to three paths:
- Normal-file
<?include?>(MDS021): track included paths duringfixandcheck. If a file appears twice in the chain, emitcyclic include: A.md -> B.md -> A.md. - Schema
<?include?>(new): track paths duringparseTemplate. Same diagnostic. - Catalog glob (MDS019): if a matched file includes (via MDS021) the file containing the catalog, the catalog body would contain itself. Detect by checking whether the catalog-owning file appears in any matched file's include chain.
Also add a max depth (default 10) as a safety net. Depth exceeding the limit is an error even without a detected cycle.
Rename the config key in required-structure settings. No deprecation; update all config and docs in this PR.
# Before
required-structure:
template: plan/proto.md
# After
required-structure:
schema: plan/proto.mdWhen <?require?> appears in a file that is
not being parsed as a schema, emit:
MDS020 <?require?> is only recognized in
schema files; this directive has no effect here
Add visited-file tracking to MDS021 include resolution inDone.internal/rules/include/.ExtendDone.parseTemplateininternal/rules/requiredstructure/rule.goto process<?include?>directives.Add diagnostic forDone.<?require?>in non-schema files.RenameDone.templatetoschemain required-structure config.Update fixtures.Done.Update docs.Done.RunDone.mdsmith check .to verify.
- Schema files can use
<?include?>to pull in heading fragments - Cycle detection works for normal-file includes (direct and indirect)
- Cycle detection works for schema-file includes
- Max include depth (10) is enforced
-
<?require?>in a non-schema file emits a warning - Config key is
schema, nottemplate - All
.mdsmith.ymloverrides updated - All tests pass:
go test ./... -
go tool golangci-lint runreports no issues -
mdsmith check .passes