Beagle SCM
GET-053: bare get goes FF/FB-only; get! becomes the any-commit reset
RULED into /wiki/GET (gritzko, 2026-08-01): a bare get may only move the wt ALONG cur's line — fast-forward or fast-backward, "refuses otherwise" (/wiki/GET "Forceful execution"); diverged and unrelated targets need the bang, which resets to any commit, discards local changes and deletes untrackeds. Divergence resolves through two loud doors: patch (weave theirs in) or get! (adopt theirs). Kills the diverged-plain-reset machinery of GET-048 and the silent non-FF clobber class of GET-014.
Context
Today bare get moves the wt to ANY ref: a diverged target is a plain reset (GET-048), needing merge-base basePaths classification and still risking silent loss (GET-014, GET-052); get! means "discard dirty edits + sweep untracked" (/wiki/GET §2.2, GET-040).
- Line-only makes bare
get provably lossless: oldTip and target are ancestor-related either way, so the weave has a true common base and never a merge-base fold.
- A diverged peer can land objects but can never move the wt onto its own line (the GET-014 clobber class dies structurally); a BEHIND peer still can, see the open question below.
get '#~1' rewind is a fast-backward, so it STAYS a bare form (the earlier "rewind is PUT's ground" reading is retired by the 2026-08-01 spec).
Goals
Land the line gate in be/verbs/get/get.js (and the native twin); the spec is written and the bang semantics are ruled, so the code is the only thing left.
- Bare
get and every fetch-leg update refuse an OFF-LINE target (diverged, unrelated) in plain words: "not on this line — patch to merge, get! to reset"; ancestor and descendant targets both pass.
- RULING (gritzko 2026-07-29, from the GET-057 incident; extended 2026-08-01): staged rows STAY across any bare get, forward or backward — no silent
...V → ...v demotion; only get! clears them.
- RULING (gritzko 2026-08-01) on a BEHIND remote: the network leg does NOTHING — a stale peer's older tip is a no-op, not a rewind and not a refusal, so the GET-019 class stays dead; fast-backward is a LOCAL motion.
get! reaches any commit: diverged, unrelated, backward detach (/wiki/GET "not necessarily fast-forward or fast-backward").
- The refusal fires BEFORE the wtlog append — today every seed appends the get row first, then fans out; pre-append refusal also serves the GET-016/GET-018 atomicity line.
Constraints
Concerns raised in review (2026-07-24); the first two are settled by the 2026-08-01 spec, the third stands:
- SETTLED nuke-by-habit: fast-backward is bare now, so only a genuinely DIVERGED switch demands the bang — the everyday
git checkout feat analogue no longer routes through the destructive door.
- SETTLED subs: a parent moving a sub's pin BACKWARD is a fast-backward, a bare-legal motion; the GET-047 §4.1 pin-attached update stays declarative regardless.
- CONCERN
get! reach: "any ref" includes unrelated history — crossUpdate's unconditional unrelated refusal (get.js:441) would need a force override, reopening the "another project in the cell's clothes" hazard GET-047 guards; sweepUntracked on an unrelated-ref jump approximates rm -rf of the wt, making the GET-051 pre-discard guard load-bearing.
WIP
Design decisions
The bang-semantics split is RULED (c) by /wiki/GET 2026-08-01; (a) and (b) are dead:
- RULED (c): one bang, one meaning — any commit AND discard (reset to a clean state, local changes dropped, untrackeds deleted); no separate destruction-only form.
- (a) dead: the bang stays destructive; the spec binds "any commit" and "clean state" in the same sentence.
- (b) dead: the line gate is not scoped to same-track updates — a branch switch obeys it too, and stays bare as long as the target is on cur's line.
The FAST-BACKWARD case in GET-058 terms (the landed quad classifier decides, this only says which column feeds it):
- The deciding axis is TARGET vs BASE, never "track vs root" literally; the wt column stays measured vs BASE in both directions (GET-058).
- FAST-FORWARD: root = mergeBase = base, so the track column IS target-vs-base — the 13 cells are indexed exactly as GET-058 wrote them.
- FAST-BACKWARD: nothing to add —
checkout.trackChar(oldSha, newSha) already computes that axis directly, so the backward cells (o→delete, x→restore) fall out of the landed code (VERIFIED 2026-08-01: test/get/line-backward was green before any edit).
- So no inversion helper was written; the backward test stays as a regression guard on
trackChar.
- Both columns lit at once = DIVERGED = refuse (bare) — the shape the table never has to fold, as GET-058 states.
- Nothing else moves: same 13 cells, same WEAVE3/OURS/THEIRS/DELETE verdicts, same binary modifier on the both-sides-edited cells.
TODOs
- gritzko rules on the bang split — (c), /wiki/GET 2026-08-01;
get! vs UNRELATED history still open (does the GETCELL refusal at get.js:441 survive force?)
- line gate: compute
relate.verdict in every seed BEFORE appendWtlog (seedLocal, seedRemote, handleWtSeed, crossUpdate, inRepoSeed) — refuse diverged/unrelated pre-append, PASS behind
- fast-backward axis: no work needed,
checkout.trackChar is already target-vs-base (Design decisions 3-4)
- plain-words refusal message ("not on this line —
patch to merge, get! to reset"), never a bare code
- bare
get '#~1' and backward detach ?<sha>/#<sha> KEEP working (fast-backward); the bang is needed only off-line
- sub carve-out: §4.1 pin-attached sub updates stay declarative (never line-checked); the §4.2
get! override unchanged
- drop the GET-048 diverged machinery (
basePaths, merge-base del classification) from the non-force path
- GET-051 pre-discard size guard lands before (or with) the widened
get!
- staging survives any bare get: classify/wtlog row-liveness keeps put rows across the new get tip row (ruling above); tests = stage, FF get, still
...V; stage, FB get, still ...V; get! clears
- network leg: a behind fetched tip is a NO-OP (objects land, wt and cur do not move) — no wtlog get row, no refusal text
- tests: off-line refusal cells added to the GET-047 matrix, plus a fast-BACKWARD accept cell; same-tip recovery and detached recovery stay green
- spec: /wiki/GET intro + "Forceful execution" written by gritzko 2026-08-01; /wiki/Verbs cheat sheet (
git checkout feat row) still to check — his page, request his edit
- the GET-058 quad classifier stays as landed; this ticket only adds the fast-backward axis derivation (Design decisions) — no second mechanism
- mark GET-048 STALE with a pointer here once the gate lands (GET-052's hazard dies with it)
Blockers and bummers
- Unblocked (bang split ruled); the gate itself is small, but its pre-append placement touches every seed and overlaps the GET-018 atomicity design.
Outcome
(open)