get DELETES untracked files — only get! may cleanA plain (non-force) jab get must MERGE dirty tracked files and LEAVE UNTRACKED
files alone; only jab get! may clean the worktree (GET wiki: "be get!
resets the worktree to a clean state, all local changes discarded"). It does not:
a non-force get that recurses into a submodule RESET the sub and SILENTLY
DELETED every UNTRACKED file in it. Verified this session: in a beagle-ext
worktree, test/ is the beagle-ext-tests store-backed submodule; a non-force
jab get swept 7 dirs of untracked, newly-authored test files (left empty),
while tracked files survived — silent data loss. Suspected locus: get's del-sweep
/ prune (verbs/get/get.js fanoutWholeTree + delSweep) unlinks worktree
paths absent from newTree WITHOUT checking they were TRACKED (in oldTree),
and/or the submodule recursion does a wholesale clean re-checkout of the sub
(should be read-only, DIS-058). JS only (be/, via jab). Evidence: cost the
GIT-016 T1–T4 tests. Method: work; siblings JGET-001/DIS-058/JGET-013.
beagle-ext worktree test/ is a SEPARATE store-backed submodule (beagle-ext-tests, own .be). A NON-force jab get (recursing) reset that sub and DELETED all UNTRACKED files under it — 7 test dirs emptied; the 40+ TRACKED test files survived.verbs/get/get.js fanoutWholeTree(ctx, r, wt, force): noPrune = !!force || (sameTip && !r.fresh); a del-sweep BARRIER (DELSWEEP/delSweep) folds delete leaves; if (!force) dirtyOverlapCheck(...) (get.js ~L382). The prune/del-sweep is what removes worktree files during a switch/update.be get! (force) "resets the worktree to a clean state, all local changes discarded" — so a non-force get must NOT discard untracked (or dirty tracked) content.oldTree NOR newTree. A tracked deletion = a path in oldTree but gone from newTree. Only the latter may be unlinked, and only when not dirty.jab get (switch / update / re-get / FF) NEVER deletes an untracked worktree path (in neither tree); it merges dirty tracked files and only removes TRACKED deletions.jab get! keeps its clean-reset (may remove untracked/dirty) — force is the SOLE cleaning path.be/, run via jab. Do NOT run native be; do NOT open beagle/.get.GET-040 comments, greppable, ticket-coded ≤64-char commit. No new deps.get! (still cleans), dirty-tracked merge, tracked-deletion sweep, or the get/dirty/restore buckets.oldTree"; a path in neither tree is untracked → LEAVE IT. Never unlink by "absent from newTree" alone.get!) is the only mode that may remove untracked/dirty; everything non-force is merge-or-leave.test/sub/untracked/run.sh (sub + nested untracked + dirty tracked preserved non-force; force arm cleans). Top-level untracked is safe by construction: the tree-reconcile enumerates only tree entries and a delete leaf fires only with an oldSha (get.js leaf/reconcileDir).shared/checkout.js apply gates unlinks on the OLD baseline tree (oldMap) — non-force unlinks only tracked deletions, preserves dirty edits (mrg). 0092271c.shared/submount.js threads the sub's PRIOR pin as oldTip + the global ambient force flag into checkout.apply; recursion no longer clean-resets the sub. 0092271c.get! clean-reset asserted by test/sub/untracked arm 2; suite green per d80bdb5c "js tests pass, no native use" (2026-07-06 audit: not re-run, read-only).--force orphan sweep); coordinate so force still cleans and non-force never does.0092271c "GET-040: fix recursion bug (submodule reset)": FORCE (get!) is the sole clean-reset; NON-force (re)writes only missing/clean tracked files, preserves dirty edits, and unlinks ONLY a tracked deletion — an untracked path (in neither tree) is never touched (shared/checkout.js apply oldTip/oldMap model; shared/submount.js passes the sub's prior pin + the global force flag).test/sub/untracked/run.sh (two arms: non-force preserve, force clean-reset).50e90012); suite not re-run (read-only audit) — green per d80bdb5c.//DIS-082-jab, evidence tree frozen)post ?#5d31a147 moved the base, so the 11:32 bare jab get '///jab/' ran the DIVERGED path; verbs/get/get.js treats ANY diverged non-force get as a GET-048 switch-reset — deletes ours-only files, resurrects ours-deletes, clean-overwrites clean-vs-local-head files to theirs. Correct for an explicit ?br switch, data loss for a track UPDATE. The 0092271c guards are intact and were never the locus.//GET-040 (get.js only, NOT landed): a diverged non-force TRACK UPDATE (trackUpdate flag: bare get, wt-operand/cross-source update, track re-fetch) 3-way merges against the MERGE-BASE tree — local-only adds stay, ours-deleted+theirs-unchanged stays deleted, theirs-unchanged keeps ours, theirs' changes weave. Explicit ?br/?sha/pin switches keep GET-048 reset.oldTipOf() (get.js:145): only get/post rows move the base — a staged sub pin put dog#<sha> could read back as the parent's old tip.test/get/divergent-track-update/run.sh RED pre-fix, GREEN after; ctest 328/328 (orchestrator re-ran the divergent bucket 6/6).5d31a147 is intact in the main jab store (jab cat verified cfold.hpp, test/weave.js, test/bass.js bytes; weave.hpp correctly absent); dog sub commit e118a411 + pin and all 17 put/delete rows still sit in the incident wt's ulog — that ground is GET-053's.GET-040: diverged track update merges vs merge base, never resets