Skip to content

T-3K8K-templates-html-comment-after-frontmatter

Status: closed/done · Impact: medium · Complexity: small

Auto-generated from a /sdlc:task-work post-mortem; reframed via PR review. Review and promote to ready before picking up.

plugin/entities/*/template.md files currently start with an HTML comment block, followed by the YAML frontmatter, followed by the body. This ordering breaks standard markdown frontmatter parsers (Hugo, Jekyll, Astro, MDX, Pandoc with --yaml-frontmatter, etc.) — all of them require the --- fence at line 1 to treat the block as frontmatter. The original post-mortem proposed relaxing validate_frontmatter.py to skip the leading comment; on PR review (#18) we flipped the framing: fix the templates instead, since non-standard ordering bites every downstream tool, not just our validator.

Cited by T-J2CW-add-epic-entity-task-depends-on-dependencies.

From the originating post-mortem:

Template files (plugin/entities/*/template.md) fail validate_frontmatter.py directly because the HTML comment block precedes the frontmatter and the parser’s regex is anchored to \A---.

Validator: plugin/validators/validate_frontmatter.py — front-matter regex is anchored to \A---. Correct as-is: standard markdown frontmatter must be the first thing in the file.

Strip-comment regex in the new_*.py scripts: HTML_COMMENT_AT_TOP_RE = re.compile(r"\A<!--.*?-->\s*\n", re.DOTALL) — also anchored to \A. When templates are reordered, this regex must move its target from “before frontmatter” to “after the closing --- fence.”

Affected templates:

  • plugin/entities/task/template.md
  • plugin/entities/milestone/template.md
  • plugin/entities/backlog/template.md
  • plugin/entities/epic/template.md

Affected scripts (strip-comment logic):

  • plugin/scripts/new_task.py
  • plugin/scripts/new_milestone.py
  • plugin/scripts/new_epic.py
  • plugin/scripts/new_backlog.py (verify; may or may not have one)

Each template starts with its YAML frontmatter at line 1. The HTML comment block lives between the closing --- fence and the body’s # Title line:

---
type: task
# ... frontmatter ...
---
<!--
Task template guidance. Delete this comment block once the file is in
use. (Same content as today, just relocated.)
-->
# <Title>
## Goal
...

Each new_*.py script’s strip-comment regex is updated to find and remove the comment from its new location (after the closing --- fence, before the title). Templates pass validate_frontmatter.py in place without any validator changes.

  1. Move the HTML comment block in all four templates from line 1 to between the closing --- and the # <Title> line. Preserve the comment’s content verbatim.
  2. Update each new_*.py script’s HTML_COMMENT_AT_TOP_RE (and any strip-comment logic) to locate the comment after the frontmatter. Suggested rename to TEMPLATE_HEADER_COMMENT_RE for clarity; the regex needs to allow optional whitespace between --- and <!--.
  3. Verify by running each new_*.py script against its updated template in a /tmp project root and confirming the generated file has the comment fully stripped.
  4. Add a regression test: validate_frontmatter.py against every shipped plugin/entities/*/template.md exits 0.
  • plugin/entities/task/template.md — move HTML comment block after frontmatter.
  • plugin/entities/milestone/template.md — same.
  • plugin/entities/backlog/template.md — same.
  • plugin/entities/epic/template.md — same.
  • plugin/scripts/new_task.py — update strip-comment regex location.
  • plugin/scripts/new_milestone.py — same.
  • plugin/scripts/new_epic.py — same.
  • plugin/scripts/new_backlog.py — same if it has comment-stripping logic.
  • (Maybe) plugin/validators/tests/ — regression test that every template validates in place. Path uncertain; the validator may not have a tests dir yet.
  • AC-1: Each plugin/entities/*/template.md file starts with --- at line 1 and contains its HTML comment block AFTER the closing frontmatter --- fence, BEFORE the # Title line.
  • AC-2: Running validate_frontmatter.py against every shipped plugin/entities/*/template.md exits 0.
  • AC-3: Running each new_*.py script produces an output file with no leading or post-frontmatter <!-- ... --> comment block (the script still strips the relocated comment correctly).
  • AC-4: A common third-party frontmatter parser (e.g. Python’s python-frontmatter package or yaml.safe_load after a manual ----split) successfully parses the frontmatter from each template with no preprocessing.
  • Validator changes (the validator’s \A--- anchor is correct; we’re making the templates standards-compliant, not the validator more lenient).
  • Substantive rewrites of the comment contents (move only — content preservation is required).
  • Re-architecting the templates to a different format entirely (e.g. moving guidance to a sibling template.guide.md file).
  • none

Spawned by /sdlc:task-work post-mortem of T-J2CW-add-epic-entity-task-depends-on-dependencies on 2026-05-19. Originally framed as “relax the validator.” Reframed on the same day via PR #18 review: the templates are non-standard markdown; the validator isn’t wrong. Standards-compliance fix is cleaner and removes the documentation-vs-code-tolerance tension entirely.

Captured by /sdlc:task-work on 2026-05-20. PR: pending.

  • AC-1: agent-manual — ran a Python script in-worktree confirming each template starts with ---, has its <!--...--> block after the closing frontmatter fence, and the next non-whitespace content after the comment is # Title.
  • AC-2: auto — plugin/validators/validate_frontmatter.py against all four templates returns 4/4 file(s) passed.
  • AC-3: agent-manual — ran each new_*.py (task, milestone, epic, backlog) against a fresh /tmp project root; generated files contain zero <!-- substrings and validate clean.
  • AC-4: agent-manual — yaml.safe_load after a ----split (the same shape downstream tools like python-frontmatter use) returns the expected type: field for all four templates.
  • The validator’s own \A--- anchor immediately surfaced the second issue (placeholder created: YYYY-MM-DD was no longer hidden behind the leading HTML comment). One run of the validator made the gap obvious.
  • Moving the strip-comment step to operate on the post-frontmatter body (rather than the full text) kept the regex simple — no need to walk past the frontmatter manually.
  • Task spec didn’t anticipate that templates becoming validator-visible would expose created: YYYY-MM-DD as a schema violation — had to invent the '1970-01-01' sentinel mid-implementation. Task authoring guidance for “make this file validate” tasks should require a check for placeholder values that the schema would reject, before listing the change as small/medium complexity. → T-SJBT-task-spec-flags-schema-rejecting-placeholders
  • AC-2 implicitly required templates to satisfy ALL of their schema (status enum, date pattern, etc.), not just the leading---- structural requirement. The AC could be tighter: either “validates structurally (frontmatter detected)” vs “validates fully against schema” — different bars.
  • Approach step 4 (“Add a regression test”) had Files to touch flag the path as “uncertain” — skipped here as out of scope for a small task, but a follow-up task to wire check_entities.py (or a sibling) to also assert “all shipped templates validate clean” would close the AC-2 gap with a CI signal rather than relying on the agent to remember. → T-S99C-project-check-asserts-shipped-templates-validate
  • Worktree init (mise trust && just setup-worktree) is a step the task-work skill prescribes universally, but this docs/plugin-only repo has no justfile, no package.json, no lefthook config — the step is a no-op here. The skill could detect “no justfile present” and skip with a one-liner instead of asking the agent to figure out it’s a no-op. → T-FC8W-task-work-graceful-no-justfile-fallback

← Back to Tasks