Skip to content

Keep multi-line agent notes intact through emit and parse - #40

Merged
fohara merged 1 commit into
mainfrom
fix/agent-note-newline
Aug 9, 2026
Merged

fohara merged 1 commit into
mainfrom
fix/agent-note-newline

Conversation

@fohara

@fohara fohara commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Replaces #37, which GitHub closed when its base branch was deleted on merge of
#36. Same change, rebased onto main.

What was wrong

lash add --agent-note "$(printf 'first\nsecond')" wrote
@agent-note: first followed by a bare, unindented second. The parser
treats an unindented line as the end of the annotation block, so lash show
afterwards returned only "first" and the rest of the note was gone, exit code 0.

I had previously assessed this as unreachable from the CLI on the grounds that
notes are single-line. That was wrong; the shell one-liner above is all it
takes.

format_task_annotations built the note with a single format! and pushed the
result as one "line", so any newline in the value landed in the file with no
indentation.

The fix

The emitter writes the first line as the @agent-note: line and each
continuation with the annotation indent, which is the shape
parser::annotations::parse_annotation_block folds back into one value.

Indentation alone is not enough for every value, because the folding is lossy
in two ways. A blank line is skipped, so a note containing one comes back with
the gap closed up. A line starting with @ is read as the start of a new
annotation, truncating the note and possibly inventing an annotation nobody
asked for. MarkdownEmitter::check_agent_note rejects both at validation time
with E_CREATE_INVALID_AGENT_NOTE, naming the offending line, and leaves the
file untouched.

Leading whitespace on a continuation line is normalized rather than rejected.
The parser trims it, so indentation is lost but no text is, and rejecting it
would rule out ordinary things like an indented list inside a note.

Tests

A round-trip property test over notes of one to four lines at four indent
levels, emitting through format_task_annotations and reading back through
parse_annotation_block. Unit tests for the accept and reject sets and for
continuation indentation. Three integration tests covering a two-line note
surviving through to lash show, and both rejection cases leaving the file
byte-identical.

`lash add --agent-note "$(printf 'first\nsecond')"` wrote
`  @agent-note: first` followed by a bare, unindented `second`. The parser
treats an unindented line as the end of the annotation block, so `lash
show` afterwards returned only "first" and the rest of the note was gone,
with exit code 0.

`format_task_annotations` built the note with a single `format!` and
pushed the result as one "line", so any newline in the value landed in
the file with no indentation. It now writes the first line as the
`@agent-note:` line and each continuation with the annotation indent,
which is the shape `parser::annotations::parse_annotation_block` folds
back into one value.

Indentation alone is not enough for every value, because the folding is
lossy in two ways. A blank line is skipped, so a note containing one
comes back with the gap closed up. A line starting with `@` is read as
the start of a new annotation, truncating the note and possibly
inventing an annotation that was never intended.
`MarkdownEmitter::check_agent_note` rejects both at validation time with
`E_CREATE_INVALID_AGENT_NOTE`, naming the offending line, and the file is
left untouched. That is the "reject rather than write something the
parser will silently truncate" half of the fix.

Leading whitespace on a continuation line is normalized instead of
rejected. The parser trims it, so indentation is lost but no text is, and
rejecting it would rule out ordinary things like an indented list inside
a note.

Tests: a round-trip property test over notes of one to four lines at four
indent levels, emitting through `format_task_annotations` and reading
back through `parse_annotation_block`; unit tests for the accept and
reject sets and for continuation indentation; and three integration tests
covering a two-line note surviving to `lash show`, and both rejection
cases leaving the file byte-identical.
@fohara
fohara merged commit 698d642 into main Aug 9, 2026
21 checks passed
@fohara
fohara deleted the fix/agent-note-newline branch August 9, 2026 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant