Skip to content

Fix lash add splicing into a preceding multi-line annotation - #33

Merged
fohara merged 2 commits into
mainfrom
fix/add-multiline-annotation-data-loss
Aug 9, 2026
Merged

fohara merged 2 commits into
mainfrom
fix/add-multiline-annotation-data-loss

Conversation

@fohara

@fohara fohara commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

What was wrong

When the last task in the target file carried a multi-line @agent-note,
lash add inserted the new task after the first line of that note. The
orphaned continuation lines were then destroyed on the next reindex. Exit code
was 0 with a normal success message, so nothing signalled the loss.

PlacementResolver::count_annotation_lines counted every annotation as exactly
one source line. The parser folds indented continuation lines into the
annotation value and joins them with \n, so a three-line note is one
Option<String> holding two newlines while occupying three lines on disk.

The fix

Count actual source lines with value.lines().count().max(1), applied to every
single-value annotation the parser can fold: @agent-note, @owner,
@estimate, and custom annotations. The .max(1) floor matters, since an empty
value has lines().count() == 0 but still occupies its own @key: line.

Same class as the @id undercount fixed for issue #24. The one-line-per-
annotation assumption was never revisited for values that can wrap.

Tests

Four unit tests in placement.rs (multi-line note, single-line note, empty
note, multi-line owner/estimate) and two end-to-end tests in
add_command_test.rs covering note contiguity in the file and survival through
reindex. The multi-line tests were confirmed to fail without the fix.

Not fixed here

Two related defects found while verifying are filed as follow-ups in
lash.index.md, along with three more found afterwards. The @depends-on
comma form over-counts and can append outside the tasks section, and
lash add --agent-note with an embedded newline emits an unindented
continuation the parser silently drops.

fohara added 2 commits August 8, 2026 14:57
`lash add` spliced a new task into the middle of a preceding task's
multi-line `@agent-note`, and the orphaned continuation lines were then
destroyed on the next reindex — silent data loss with a success message
and exit code 0.

`PlacementResolver::count_annotation_lines` counted every annotation as
exactly one source line. The parser folds indented continuation lines
into the annotation value, joining them with `\n` (see
`parser::annotations::parse_annotation_block`), so a three-line note is
a single `Option<String>` holding two newlines and occupies three lines
on disk. Counting it as one put the insertion point after the note's
first line.

Count actual source lines via `value.lines().count().max(1)`, applied to
every single-value annotation the parser can fold: `@agent-note`,
`@owner`, `@estimate`, and custom annotations. The `.max(1)` floor
matters — an empty value has `lines().count() == 0` but still occupies
its own `@key:` line.

This is the same class as the `@id` undercount fixed previously (the
issue #24 regression test); the assumption of one line per annotation
was simply never revisited for values that can wrap.

Tests: four unit tests in placement.rs (multi-line note, single-line
note, empty note, multi-line owner/estimate) and two end-to-end tests in
add_command_test.rs (note stays contiguous in the file; all lines
survive reindex). Verified the multi-line tests fail without the fix.

Two related defects found while verifying are filed as follow-ups in
lash.index.md rather than fixed here: `@depends-on` in comma form
over-counts and can append outside the tasks section, and
`lash add --agent-note` with an embedded newline emits an unindented
continuation that the parser silently drops.
Two are in `lash add` itself: it prepends above the H1 when the target
file has a `## Tasks` section with nothing in it (the task lands on disk,
`lash index` never sees it, and `lash lint` passes), and the task ID it
prints on success is not the one the indexer stores, so copying it into
`lash show` or `@depends-on` fails.

The third is a test-isolation bug. `test_user_config_save_and_load` in
lash-types writes the developer's real `~/.lash/config.toml` and only
restores it if the test runs to completion. An interrupted `cargo test`
leaves `color_scheme = "Test Theme"` behind, which no theme resolves, and
every later `lash` invocation on that machine fails.
@fohara
fohara merged commit 0305925 into main Aug 9, 2026
21 checks passed
@fohara
fohara deleted the fix/add-multiline-annotation-data-loss branch August 9, 2026 14:12
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