be get silently overwrites put/patch-staged contentbe get <target> destroys uncommitted STAGED work: a path staged by be put/be
patch is OVERWRITTEN with the target's bytes when the target also changed it — no
refusal, no merge, exit 0. The dirty-overlap protection in get_overlap_step
(sniff/GET.c) only weave-merges/refuses for an UNATTRIBUTED mtime; staged
content is STAMPED (known mtime), so the classifier skips protection and the WRITE
pass clobbers it. Cost this session: landing JS-036 onto JS-035 lost the
JABCdelta registration + encode docs (commit 742c06ff shipped incomplete).
Repro + contrast + session evidence in this dir. Method: Issues.
get_overlap_step (sniff/GET.c:440-529) routes a CHANGED path
to weave-merge (GET_V_MRG, :515) or refusal (:460) ONLY when
!SNIFFAtKnown(mr) (:460/:480). A stamped path skips the block, hits
return OK (:529) with no merge/noop/unlink row → WRITE overwrites it.be put/be patch stamp the files they write, so their
staged (uncommitted) bytes look "clean baseline" to the classifier, not dirty.f=base → c1 f=theirs;
be get ?#c0; echo mine>f; be put f; be get ?#c1 → upd, f=theirs, rc 0
(mine LOST). Contrast (UNSTAMPED edit, no be put) → mrg,
f=<<<<mine||||theirs>>>> (preserved). Same input, opposite outcome.be patch ?! staged
merged js/{API.md,CMakeLists.txt,INDEX.md} (pat), then be get flipped
them to upd, discarding JS-036's edits — see JS-036. (Same capture as
BE-008, a different bug.)be get must NOT silently destroy put/patch-staged content for a path the
target changed: weave-merge it (as the unstamped arm does) or refuse clearly.--force (be get!) stays the explicit opt-in to overwrite dirty/staged bytes.sniff/GET.c overlap classifier; keep the unstamped weave-merge + no-op-skip
intact; don't regress test/get/* (esp. 49 force-reset, 09/10 atomicity).uri.fragment).get_overlap_step, a CHANGED path stamped by a PENDING put/patch row
(after the pd boundary, not a baseline get/post) must route to the dirty arm:
schedule GET_V_MRG against the staged baseline, or refuse — distinguish it
from a path stamped by a baseline get/post (safe to overwrite).test/get/<n> (both arms): staged → merge/refuse,
unstamped → still merge. Repro-first (CLAUDE.md §17). → test/get/66-staged-overlap-merge (8a1f8413).get_overlap_step to detect pending put/patch ownership vs baseline
get/post and route to merge/refuse; --force overwrites. → get_pending_stage_stamp + dirty flag (8a1f8413).test/get/49, 09, 10 green. → all green + full ctest 471/471 (see Outcome).8a1f8413* (test/get/66-staged-overlap-merge). be get
no longer silently overwrites
put/patch-staged content for a path the target also changed: such a
path is now weave-merged (same arm as the unstamped case), and
--force (be get!) remains the explicit opt-in to overwrite it.sniff/GET.c, get_overlap_step): the three
dirty-protection branches keyed on !SNIFFAtKnown(mr) now key on a
single b8 dirty = !SNIFFAtKnown(mr) || get_pending_stage_stamp(mr).
The unstamped weave-merge + clean-drift no-op-skip + --force
overwrite arms are untouched — a pending-staged path simply enters
the same arms an unattributed mtime already did.get_pending_stage_stamp, new static
helper): for a path whose mtime IS in the stamp-set, look up the
owning ULOG row via SNIFFAtRowAtTs(mtime,…). YES (dirty/staged)
iff the verb is put/patch/delete AND the row's ts is strictly
above SNIFFAtPatchFloorTs() (the latest get/post anchor) — i.e.
a pending stage after the boundary. A get/post stamp, or a
stale put/patch at/below the floor (already committed / reset away),
returns NO → safe to overwrite, unchanged behaviour. The merge
baseline stays the base-tree leaf blob (base->uri.fragment, the
common ancestor the user get'd before staging); a plain be put
row carries an empty fragment, so the base-tree blob — not the put
row — is the correct weave ancestor, and the staged wt bytes are
read from disk by get_drain_merges.repro.sh into
test/get/66-staged-overlap-merge/run.sh (auto-discovered by the
`/*/run.sh glob; ctest be-get-66-staged-overlap-merge). ARM A
(staged via be put) must mrg/refuse with mine preserved; ARM B
(unstamped raw edit) must still mrg`.upd f.txt /
f.txt = [theirs] (staged bytes LOST, rc 0) while ARM B mrg'd
(verified by reverting dirty to the bare !SNIFFAtKnown(mr) and
rebuilding). Post-fix, both arms mrg and preserve mine; test
passes under build/ and build-debug/ (ASan).test/get/49 (force-reset), test/get/09 &
test/get/10 (atomicity) all green; full `be-get- (78), the
put/patch/post/del families (114), and the whole ctest suite
(471/471) pass in build/, and get/put/patch (92) pass under
build-debug/` ASan.sniff/GET.c (new get_pending_stage_stamp
helper + the dirty-flag refactor of the three branches);
test/get/66-staged-overlap-merge/run.sh (new two-arm case). No
be/ changes (concurrent JS-065 worker).