Skip to content

set --body-file destroys the body irreversibly; no history, no empty-guard - #111

Merged
MJohnson459 merged 1 commit into
mainfrom
body-history-guard
Aug 1, 2026
Merged

set --body-file destroys the body irreversibly; no history, no empty-guard#111
MJohnson459 merged 1 commit into
mainfrom
body-history-guard

Conversation

@MJohnson459

Copy link
Copy Markdown
Contributor

voro set --body-file swapped a task's whole brief for whatever the file held,
in place, with nothing left behind — the one field an edit destroys outright and
the one whose loss cannot be reconstructed from state elsewhere. The 2026-07-29
incident (set 284 --body-file /dev/null, meant as a no-op) was unrecoverable
because the events table stores only kind/detail transitions.

Three changes, all three of the suggestions in the task body:

  • History. Every edit that changes a non-empty body records the text it
    replaced as a body event on the append-only log. It rides
    Store::update_task, so it covers all three writers — the CLI flag, a refine
    agent, and the TUI editor — and fires only on a real change, since every set
    passes through there. A body event's detail is bulk kept for recovery rather
    than reading, so show and the TUI history fold it to a marker naming the
    event that holds it (replaced body kept (37 lines) — voro show 62 --event 512) instead of unrolling a superseded brief into the log.
  • Recovery. voro show <id> --event <event-id> prints one event's detail
    alone and undecorated, so getting a body back is a redirect straight into
    set --body-file rather than a verb of its own. The event must belong to the
    task named.
  • Guard. A replacement that would leave a non-empty body empty is refused
    unless --allow-empty says so; emptying an already-empty body destroys
    nothing and passes unremarked.
  • Additive spelling. --append-body / --append-body-file add to the
    existing body after a blank line — what the agent in the incident was actually
    trying to do — and conflict with the replacing pair at the parser.

DESIGN.md §8 gains the paragraph (beside the summary's, which it mirrors: a
summary is superseded, a body is overwritten), the §5 events comment now names
replaced bodies, and the plugin skill's CLI reference names the new flags.

Verified: cargo test --workspace (270 + 1 + 274, up 5 new tests covering the
guard, the recovery round-trip, the append semantics, the store's log-only-on-
change rule, and the TUI history fold), cargo clippy --workspace --all-targets -D warnings clean, cargo fmt --all. Also driven end-to-end against a scratch
database: set 1 --body-file /dev/null is refused with the line count intact,
an append lands after a blank line, and a replaced body is recovered with
voro show 1 --event 3 > f.md && voro set 1 --body-file f.md byte-for-byte.

Branch body-history-guard is committed locally and not pushed (no push
without approval).

`voro set --body-file` swapped a task's whole brief for whatever the file
held, in place, with nothing left behind — the one field an edit destroys
outright and the one whose loss cannot be reconstructed from state
elsewhere. Working mote #286 an agent ran `set 284 --body-file /dev/null`
meaning a no-op and wiped #284's body; the events table stores only
kind/detail transitions, so the text was unrecoverable and had to be
rewritten by hand from the parent task's context.

Every edit that changes a non-empty body now records the text it replaced
as a `body` event on the append-only log. It rides `Store::update_task`,
so it covers all three writers — the CLI flag, a refine agent, and the
TUI editor — and only fires on a real change, since every `set` passes
through there. That detail is bulk kept for recovery rather than reading,
so `show` and the TUI history fold it to a marker naming the event that
holds it (`replaced body kept (37 lines) — voro show 62 --event 512`)
instead of unrolling a superseded brief into the log, and `voro show <id>
--event <event-id>` prints one event's detail alone and undecorated, so
recovery is a redirect back through `set --body-file` rather than a verb
of its own.

A replacement that would leave a non-empty body empty is refused unless
`--allow-empty` says so: nothing legitimate reads as "blank the brief",
and the way one actually arrives is a slip. Emptying an already-empty
body destroys nothing and passes unremarked. Beside the replacing pair
sits an additive one, `--append-body`/`--append-body-file`, which adds
after a blank line — the "record a finding on the task" case that was
what the agent in the incident was actually trying to do.

DESIGN.md §8 gains the paragraph, beside the summary's, and the skill's
CLI reference names both new flags.
@MJohnson459
MJohnson459 merged commit 439d05b into main Aug 1, 2026
6 checks passed
@MJohnson459
MJohnson459 deleted the body-history-guard branch August 1, 2026 20:02
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