Skip to content

fix(frontmatter): every write silently deletes all YAML comments (JSON round-trip) #491

Description

@evemcgivern

Problem

Every command that writes a track's frontmatter silently deletes every YAML comment in it. Not occasionally — structurally.

lib/frontmatter.py round-trips frontmatter through JSON:

def read_file(...):
    meta = _yaml_to_dict(match.group(1))      # yq -o=json .   -> JSON

def write_file(path, meta, body):
    yaml_text = _dict_to_yaml(meta)           # yq -P . <- json.dumps(meta)
    p.write_text(f"---\n{yaml_text}---\n{body}")

lib/frontmatter.py:19,37,41-51

JSON has no comment concept, so comments cannot survive the trip. Any writer — refresh-md, reconcile, slot, batch-slot, handoff --set-next, anything calling write_file — drops them all. Silently: no warning, no diff summary, no confirmation naming the loss.

Observed

Ran hygiene --repo=CritForge --yes (steps 1–2 write). One track lost 213 lines, including all of its # TIER 1 — / # TIER 2 — / # TIER 3 — section headers and the per-issue rationale beneath each next_up entry.

The next_up order survived perfectly — all 45 entries, correct sequence — which is what makes this dangerous. A quick sanity check on the ordering passes, and the loss is only visible if you diff the whole file or go looking for prose that is no longer there. The data survives; the reasoning does not.

Measured exposure on one repo right now: 117 comment lines across 2 tracks, 110 of them in a single launch-critical track. That is the entire written justification for a priority ordering — why each issue sits where it does, which premises were investigated and refuted, which placements were a deliberate override.

This has bitten before: an earlier incident logged slot erasing 314 lines of rationale the same way.

Why it matters more than it looks

The next_up list is a bare sequence of issue numbers. Comments are the only place the why can live inline, and the toolkit's own conventions encourage recording it there (a re-rank is worthless six weeks later if nobody can reconstruct the reasoning). So the format invites putting irreplaceable prose in exactly the place a routine weekly command destroys.

Worse, the destroying command is hygiene — the one advertised as safe, routine maintenance you run without thinking.

Options

  1. Preserve comments on write. yq can edit YAML in place (yq -i '.key = value') and retains comments on the paths it does not rewrite. Reshaping write_file into targeted yq -i assignments rather than a whole-document re-dump would keep untouched keys — and their comments — intact. Biggest change, correct fix.
  2. Warn loudly and require confirmation when the frontmatter being overwritten contains comments, naming the count. Cheap, and turns silent loss into a decision.
  3. At minimum, document it — in the README and in write_file's docstring — so nobody records rationale in frontmatter believing it is durable.

(1) is the real fix. (2) is a same-day mitigation that would have prevented today's loss.

Acceptance

  • A track carrying frontmatter comments survives refresh-md, reconcile, and hygiene with those comments intact — or, if that is deferred, the user is warned and must confirm before they are dropped.
  • A test writes frontmatter containing comments and asserts they are still present afterward.

Related

Activity

  1. evemcgivern commented on Oct 6, 2026

    @evemcgivern
    ContributorAuthor

    Shipped to dev in #492: write_file now warns with the count of comments it will drop, and lift-rationale moves rationale into a ## Ranking rationale body section that survives writes. Not done: a confirmation prompt before dropping comments (warn-only, so non-interactive callers don't block), and preserving comments through the write itself.

  2. evemcgivern commented on Oct 6, 2026

    @evemcgivern
    ContributorAuthor

    Reopening: #492 only makes the comment loss loud (warning) and adds lift-rationale. Writes still delete frontmatter comments, and there is no confirmation gate. Remaining: preserve comments through write_file, or require confirmation before dropping them.

  3. added a commit that references this issue on Oct 6, 2026
    5b332d0
  4. evemcgivern commented on Oct 6, 2026

    @evemcgivern
    ContributorAuthor

    Fixed in #497 (merged to dev): write_file keeps the original frontmatter when data is unchanged and otherwise edits only the changed keys in place with yq, verifies the result parses back to the requested data, and falls back to the old re-dump with a warning if not. Comments on an entry that is removed may still be dropped, and the warning counts them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions