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 sweepblocked:prAn open PR in this repo would close or unblock itbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions