Skip to content

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_load returns datetime.date for 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 to isoformat() 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.

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.

  1. Survey current frontmatter-touching call sites (grep "yaml.safe_load" plugin/) to confirm the API shape that covers them all.
  2. Author the helper with no third-party deps beyond pyyaml and add unit tests.
  3. Refactor at least start_task.py to use it (proves the API); leave other call sites as a follow-up unless trivial.
  4. Document the helper in plugin/conventions/ so future scripts reach for it by reflex.
LocationKindChange
plugin/validators/frontmatter_io.pynewthe helper.
plugin/validators/test_frontmatter_io.pynewunit tests for
plugin/skills/task-work/start_task.pymodifyadopt the helper.
plugin/conventions/frontmatter-edits.mdnewone
  • AC-1: plugin/validators/frontmatter_io.py exists with a normalize_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.py includes a test that loads last_reviewed: 2026-05-19 and asserts the normalized value compares equal to the string "2026-05-19".
  • AC-3: plugin/skills/task-work/start_task.py uses the helper in place of its inline isinstance(value, _dt.date) check.
  • 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 on start_task.py.
  • none

Spawned by /sdlc:task-work post-mortem of T-VFGF-extract-start-task-script on 2026-05-21.


← Back to Tasks