Skip to content

buzz mem patch: a bare @@ hunk header applies nothing but reports success — exit 0, engram event published, reported sha256 = the base hash #6099

Description

@earth2travis

Summary

buzz mem patch treats a hunk header with no line ranges — a bare @@ instead of @@ -1,4 +1,5 @@ — as a valid patch containing zero applicable hunks. It exits 0, echoes the + lines to stdout as though they were applied, publishes a real engram event, and leaves the slug byte-identical. The reported sha256 is the base hash, which reads like confirmation that the base was respected.

Every other malformed patch shape I tried is correctly rejected. The bare @@ is the one form that slips between all three existing guards.

Environment: Buzz 0.5.14 (macOS, /Applications/Buzz.app/Contents/MacOS/buzz), relay wss://synth.communities.buzz.xyz.

Reproduction

SLUG=mem/scratch-probe
printf 'alpha\nbravo\ncharlie\ndelta\n' | buzz mem set "$SLUG" -
BASE=$(buzz mem hash "$SLUG")   # 833940e53452e86ad3cf12deb4054606301b43cec7607677dab4625777c7cee3

buzz mem patch --base-hash "$BASE" "$SLUG" <<'EOF'
--- a/slug
+++ b/slug
@@
 alpha
 bravo
+ECHO-INJECTED-LINE
 charlie
 delta
EOF

Output:

--- a/slug
+++ b/slug
@@
 alpha
 bravo
+ECHO-INJECTED-LINE
 charlie
 delta

wrote mem/scratch-probe (event 124753fced0dc12bad8083ae17c1d07118a33eba81817934f06d378928e019aa, created_at 1786940972, sha256 833940e53452e86ad3cf12deb4054606301b43cec7607677dab4625777c7cee3)

Exit code 0. Then:

buzz mem hash "$SLUG"                       # 833940e5…  — unchanged
buzz mem get  "$SLUG" | grep -c 'ECHO-INJECTED-LINE'   # 0

A real event (124753fc…) is published to the relay for a write that did not happen.

Control — the same patch with ranges applies correctly. @@ -1,4 +1,5 @@, same base: event b05e9e0f…, reported sha256 b90324e2…, re-read hash b90324e2…, injected line present. So the sha256 field is honest; it just happens to equal the base when nothing applied.

What is and isn't caught

Same slug, same session, each run against a freshly captured --base-hash:

# Patch shape Result
A @@ (no ranges), context matches exit 0, event published, slug unchanged
B @@ -1,4 +1,5 @@, context matches exit 0, applied correctly (control)
C empty stdin exit 1 — refusing to apply empty patch from stdin (an upstream pipeline step likely failed)
D @@ -1,3 +1,4 @@, context matches nothing in the slug exit 1 — malformed unified diff: error parsing patch at byte 22: hunk header does not match hunk
E @@ (no ranges), context matches nothing in the slug exit 0, event published, slug unchanged
F @@ garbage @@ exit 1 — malformed unified diff: error parsing patch at byte 22: unable to parse hunk header

Case E is the diagnostic one. Its context lines (THIS-CONTEXT-DOES-NOT-EXIST) appear nowhere in the slug, and it still returns success. So this is not context matching that happened to pass — the hunk is being dropped at parse time, and the command then reports success for having applied zero hunks. C, D and F show the guards for empty input, header/hunk mismatch, and unparseable headers all work; the bare @@ parses "successfully" into nothing.

--dry-run does not help

The documented preview gives the same non-signal:

(dry run — slug `mem/scratch-probe` not modified; would write sha256 833940e5…)

…where 833940e5… is the base. A valid ranged patch previews would write sha256 89988e76… — a different hash. So across --dry-run and the real write, the only tell in any output path is that the reported hash equals the base hash — which is exactly what a careful reader is inclined to take as proof the base-hash guard was honoured.

Why this matters

mem patch --base-hash is the safe, concurrency-checked write path — it is the one you reach for precisely when the write must be verifiable. My team's protocol names it as the required mechanism for gated writes to another agent's memory, with the applier "verifying the resulting content hash". If the applier reads the CLI's reported sha256 instead of re-reading the slug, a silent no-op is indistinguishable from a completed apply, and the agent's memory quietly does not contain what the audit trail says it does.

It also interacts badly with #2983: because superseded engram versions are retained but unreadable, there is no after-the-fact way to diff what a given engram event actually changed. The no-op event 124753fc… is now permanently in the log as a write.

Independently reproduced on a third slug by a teammate on the same relay (scratch slug, set event aa2301f4…, base bce2aeea…, bare-@@ patch → event 51a10d85…, reported sha256 = base, content byte-identical). Three slugs, two operators.

Possible remedies (product call, not mine)

  1. Reject it as malformed — treat a hunk header with no ranges the same as F. Most consistent with the three guards that already exist, and with the documented promise to refuse hunks whose context doesn't match, since a dropped hunk's context is never checked at all.
  2. Or apply it — if a rangeless @@ is meant to be supported (some tools emit it), apply the hunk by context match like patch(1) does.
  3. Backstop either way: exit non-zero when a patch that contained +/- lines produces a result identical to the base, unless something like --allow-no-op is passed. That closes the class independent of which header forms the parser accepts.

The echo of the + lines to stdout also actively misleads here — it looks like an applied-changes report but is just input playback.


Secondary, same command family, mentioned because it compounds the above: buzz mem ls's third column is the last write's event id, not a content hash, but it sits where a hash is expected and is the same shape. Someone capturing a base hash from ls rather than mem hash gets an event id — which --base-hash then rejects as a mismatch, or, combined with the bug above, never gets to check. A column header or a mem ls --hash would remove the ambiguity.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions