Skip to content

deferredwork: flat appender block after the last canonical DW entry is swallowed — invisible to parse_legacy and never swept #304

Description

@pbean

Summary

A flat appender block (the - source_spec: / summary: / evidence: shape the inner
bmad-dev-auto review step appends, per step-04-review.md) that lands after the last canonical
### DW-<n> entry
is absorbed into that entry's parse_ledger() span. parse_legacy() masks
canonical spans before scanning, so the flat finding is invisible to it — and therefore to the
sweep's migration/triage (sweep.py reads parse_legacy for leftovers) and to the TUI's legacy
view. The deferred finding is silently lost.

This is the work-losing failure class: the inner session did its job and recorded the defer, but
no downstream reader can ever see it.

Repro (current main, a82be01)

from bmad_loop.deferredwork import parse_ledger, parse_legacy

text = (
    "# Deferred Work\n\n"
    "### DW-1: canonical\n\n"
    "origin: test\nlocation: n/a\nreason: test\nstatus: open\n\n"
    "- source_spec: `spec-next.md`\n"
    "  summary: later flat finding\n"
    "  evidence: must not be swallowed by DW-1\n"
)
assert any("later flat finding" in e.body for e in parse_ledger(text))  # swallowed into DW-1
assert parse_legacy(text) == []                                          # invisible to the sweep

Expected: the flat block is not part of DW-1's body, and parse_legacy returns one entry titled
later flat finding.

Root cause

parse_ledger() ends an entry's span only at the next heading (HEADING_RE / ANY_HEADING_RE).
The flat appender writes a bullet block with no heading, and it appends at EOF — which is exactly
where the last canonical entry's span extends to. Any ledger whose tail is canonical (e.g. after a
migration, or after the orchestrator refiles a follow-up entry) then swallows every subsequent
inner-session defer.

Fix

PR #274 (@Haven2026) has the right fix: truncate the canonical span at the first flat appender
block, so the block stays visible to parse_legacy. The PR needs a rebase onto post-#300
deferredwork.py and a scope trim (review on the PR has details); its validation commits are
split out to a separate hardening issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:deferred-workdeferred-work.md ledger and sweepbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions