T-07DV-frontmatter-edit-helpers-normalize-yaml-dates
Status: closed/obsoleted · Impact: medium · Complexity: small
Auto-generated from a /sdlc:task-work post-mortem. Review and
promote to open/ready before picking up.
Scripts that mutate task frontmatter (new_task.py, start_task.py,
ensure-ready, migrations) all round-trip through yaml.safe_load /
yaml.safe_dump. PyYAML auto-parses bare ISO dates into
datetime.date objects, so any “did this field change?” comparison
that uses a string on one side and the parsed-value on the other
silently fails. This class of bug bit
T-VFGF-extract-start-task-script in its first test run
(idempotency check fired a spurious commit). Closing the gap once,
in a shared helper, prevents every future frontmatter-mutating
script from re-discovering the same trap.
“The first run of the test suite caught a real YAML-date round-trip bug (
yaml.safe_loadreturnsdatetime.datefor bare ISO dates; the script was comparing against a string and so flagged a spurious semantic change). Lesson: when a script edits frontmatter, always normalize dates toisoformat()before comparing.”
Each frontmatter-touching script today rolls its own load + edit +
dump. plugin/skills/task-work/start_task.py now normalizes by hand;
plugin/scripts/new_task.py doesn’t need to (it writes fresh, never
compares); plugin/skills/task-ensure-ready (the implementing
sub-skill) and the v1-to-v2 migration each handle YAML loading on
their own. No shared helper exists.
Proposed
Section titled “Proposed”A small helper module — likely at
plugin/validators/frontmatter_io.py or
plugin/scripts/frontmatter_io.py — exposes load(path) -> dict,
dump(fm, original_text) -> str, and normalize(fm) -> dict
functions. The normalize step coerces datetime.date /
datetime.datetime fields back to their ISO string form so equality
checks against "YYYY-MM-DD" literals behave as a reader expects.
Existing frontmatter-mutating scripts adopt the helper instead of
inlining their own yaml round-trip.
Approach
Section titled “Approach”- Survey current frontmatter-touching call sites
(
grep "yaml.safe_load" plugin/) to confirm the API shape that covers them all. - Author the helper with no third-party deps beyond
pyyamland add unit tests. - Refactor at least
start_task.pyto use it (proves the API); leave other call sites as a follow-up unless trivial. - Document the helper in
plugin/conventions/so future scripts reach for it by reflex.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
plugin/validators/frontmatter_io.py | new | the helper. |
plugin/validators/test_frontmatter_io.py | new | unit tests for |
plugin/skills/task-work/start_task.py | modify | adopt the helper. |
plugin/conventions/frontmatter-edits.md | new | one |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1:
plugin/validators/frontmatter_io.pyexists with anormalize_for_comparison(fm)(or equivalent) function that coerces YAML-parsed date/datetime fields to their ISO string form. - AC-2:
plugin/validators/test_frontmatter_io.pyincludes a test that loadslast_reviewed: 2026-05-19and asserts the normalized value compares equal to the string"2026-05-19". - AC-3:
plugin/skills/task-work/start_task.pyuses the helper in place of its inlineisinstance(value, _dt.date)check.
Out of scope
Section titled “Out of scope”- Refactoring every frontmatter-touching script in one go — adopting
the helper across
new_task.py, ensure-ready’s stamp logic, and the migration scripts can land as follow-ups once the API is proven onstart_task.py.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Spawned by /sdlc:task-work post-mortem of T-VFGF-extract-start-task-script on 2026-05-21.