| id | 244 |
|---|---|
| title | Structured list projection; fix nested-item text corruption |
| status | ✅ |
| summary | Fix flat list projection concatenating nested-item text into the parent with no separator, then add `projection: tree` so list items project as objects with `text`, a `checked` bool for task items, and recursive `children`. |
| model | opus |
| depends-on |
kind: list projects items as flat strings. The flattening
is corrupt. A nested child's text concatenates into its
parent with no separator. Inline markup is silently
stripped. For
- [x] done item
- [ ] open item with **bold**
- nested childextract emits
"items": [
"[x] done item",
"[ ] open item with boldnested child"
]boldnested child is data corruption, not just loss. Task
checkboxes survive only as literal "[x] " prefixes, nesting
is gone, and no consumer can recover any of it.
This plan has two parts. First, an unconditional bugfix: a flat item projects its own text only — children are excluded, word boundaries survive. Second, an opt-in structured mode:
content:
- { kind: list, projection: tree }projects each item as an object:
"items": [
{ "text": "done item", "checked": true },
{
"checked": false,
"children": [
{ "text": "nested child" }
],
"text": "open item with bold"
}
]text— the item's own inline text, soft wraps joined.checked— present only on task-list items (- [x]/- [ ]); the marker leavestext.children— present only when the item nests a sub-list; recursive through the same shape.
- The flat default keeps emitting strings (compat), minus the corruption: own text only, task markers kept as today.
projection: treeis validated at schema-load time likeprojection: inline(list-only; rejected elsewhere).- Ordered-list metadata (
start, numbering) is out of scope; the item order is the array order either way. - Inline spans inside items
(
projection: tree+ span lists) ride the block grammar (plan 246), not this plan.
- Bugfix first (red/green). Failing test reproducing
boldnested child; fix flat projection to emit each top-level item's own text. Decide and test what flat mode emits for an item that is only a nested list. - Add
projection: treeto content-entry validation (list-only) and implement the recursive item walker ininternal/extract:text, optionalchecked, optionalchildren. - Task-list detection:
checkedappears only on items with a GFM task marker; the marker text never leaks intotextin tree mode. - Document both modes and the bugfix in the extract reference (default-projection table + a tree example) and add a worked example to the extract guide.
- Flat
itemsnever concatenates nested-item text into a parent string; the corrupt case above projects"[ ] open item with bold". -
projection: treeemits item objects withtext,checkedonly on task items,childrenonly when non-empty, recursive to any depth. -
projection: treeon a non-list content entry fails at config load. - YAML and msgpack formats emit the same tree.
- Reference and guide updated with verified outputs.
- All tests pass:
go test ./... -
go tool golangci-lint runreports no issues