Beagle SCM
PUT-012: sub-mount checkout skips the GET-049 restamp — phantom-dirty subs, status and put disagree
Context
Reported by gritzko 2026-07-19 in //ABC-020/ (jab clone, subs dog, dog/abc): status flags the whole grandchild dirty (12:13 ...v .gitignore etc, 7 wt), yet jab put .gitignore errs put: nothing to stage and the diff is indeed empty — the files are misstamped, not modified.
- Root cause: submount.mount checks out via checkout.apply, and checkout.materialise/writeFile never restamp — the GET-049
trySetMtime(g.stampTs) exists ONLY in get.js's root fanout (get.js:1085). Sub files keep RAW write mtimes.
- The anchor rows are same-ms-bumped: ulog.write assigns the store row the real ms and the track row +1 "honestly" (into the future). //ABC-020/dog/abc rows: store ts ..421, track ..422; every checked-out file's mtime reads ron ..421 — it can NEVER match the track row's ..422.
- Depth-1 //ABC-020/dog looks clean by LUCK: its files' write crossed into the next ms (..764) and collided with the bumped track row ts (..764). Same code path, opposite verdict — timing, not design.
- The two classifiers then split: JS classify/stage (put, diff) treats mtime ∈ stamp-set OR content-equal as clean →
put: nothing to stage; native status wants the expected stamp and marks every file v suspect → phantom 7 wt.
- Evidence probe (ulog.each + io.lstat ron ts, work/ABC-020): dog/abc/.gitignore and 01.h mtime == store-row ts ..421 exactly; dog/INDEX.md mtime == dog track-row ts ..764 exactly.
Goals
Sub-mount checkout restamps every materialised file to the sub's track-row ASSIGNED ts (GET-049 parity with the root fanout), at every depth; status then shows a fresh clone's subs clean, and status/put/diff agree.
- Red repro: fresh clone with a nested sub, then
jab status inside the grandchild — today phantom wt files whenever the checkout lands in the same ms as the anchor rows; flat/clean after.
Constraints
- Stamp with the assigned row ts via the existing trySetMtime pattern (put.js/get.js twins); be.now/appendWtlog stay the one ts source per meta norms — no log read-back.
- Symlinks stay unstamped (setMtime follows the link), matching get.js:1084.
- No behavior change to the root fanout stamping or to put's restamp.
WIP
Design decisions
TODOs
Blockers and bummers
- Wire mount branch not exercisable locally (no keeper binary); it shares the same fixed mechanism and unit-tested pieces.
Outcome
Suggested commit: PUT-012: sub-mount restamps checkout to track-row ts
Staged in work/PUT-012: shared/ulog.js, shared/checkout.js,
shared/submount.js, test/submountstamp.js, test/CMakeLists.txt.