cc776360f741
name the previous head after a combine so an agent can load the pre-combine tree back, and correct the man page's steal refusal (3.0.40, codex review pending)
- time
- 2026-09-24 04:47 UTC · 2 weeks ago
- machine
- koh2
- agent
- claude-opus-5-5
- session
- koh 3 reliability campaign
- parent
- f14336a4a1c3
↓ koh-cc776360.tar.gzatlas at this save
files changed5 files
VERSION+1 −1src/cli/cmd_steal.v+25 −22src/sync/steal.v+6 −0tests/cli_test.v+10 −1v.mod+1 −1src/cli/cmd_steal.v+25 −22
@@ -50,17 +50,18 @@5050 offered := offer_after_steal(store, mut backend)5151 if args.json() {5252 emit_json(json.encode(StolenJson{53- repo: report.repo54- forward: report.forward55- saves: report.fetched_saves56- objects: report.fetched_objects57- events: report.fetched_events58- clashes: report.clashes59- replayed: report.replayed60- kept_mine: report.kept_mine61- head: report.head62- offer_failed: !offered63- flagged: report.flagged.map(it.save_id)53+ repo: report.repo54+ forward: report.forward55+ saves: report.fetched_saves56+ objects: report.fetched_objects57+ events: report.fetched_events58+ clashes: report.clashes59+ replayed: report.replayed60+ kept_mine: report.kept_mine61+ head: report.head62+ offer_failed: !offered63+ flagged: report.flagged.map(it.save_id)64+ previous_head: report.previous_head6465 }))6566 return 06667 }@@ -76,6 +77,7 @@7677 if report.kept_mine > 0 {7778 note('kept mine for ${fmt.plural(report.kept_mine, 'file')}')7879 }80+ note('the tree from before is kept — koh load ${report.previous_head} brings it back, less anything stripped')7981 }8082 warn_flagged(report.flagged)8183 if report.clashes.len > 0 {@@ -211,15 +213,16 @@211213 }212214 213215 struct StolenJson {214- repo string215- forward bool216- saves int217- objects int218- events int219- clashes []string220- replayed int221- kept_mine int222- head string223- offer_failed bool224- flagged []string216+ repo string217+ forward bool218+ saves int219+ objects int220+ events int221+ clashes []string222+ replayed int223+ kept_mine int224+ head string225+ offer_failed bool226+ flagged []string227+ previous_head string225228 }src/sync/steal.v+6 −0
@@ -54,6 +54,9 @@5454 removed int5555 head string5656 flagged []events.Flag // incoming saves that are flagged57+ // Set when a combine took this machine's saves off the line: loading it58+ // brings back the tree from before the steal, less anything stripped.59+ previous_head string5760 }5861 5962 pub const dirty_tree = 'save or checkpoint first — steal needs a clean tree'@@ -139,6 +142,9 @@139142 }140143 store.commit() or { return err }141144 report.head = final.id145+ if local_only.len > 0 && report.fetched_saves > 0 {146+ report.previous_head = local_only.last().id147+ }142148 flags := events.latest_flags(store)!143149 for record in fetched.saves {144150 if flag := flags[record.id] {tests/cli_test.v+10 −1
@@ -697,7 +697,16 @@697697 refused := run_home(b, home, 'steal')698698 assert refused.code == 1699699 assert refused.err.contains('--mine') || refused.err.contains('--theirs')700- assert run_home(b, home, 'steal', '--theirs').code == 0700+ b_head := run_home(b, home, 'status', '--json').out.all_after('"head_id":"')[..12]701+ theirs := run_home(b, home, 'steal', '--theirs', '--json')702+ assert theirs.code == 0703+ // The pre-combine tree is off the line now; the way back must be named,704+ // or an agent without the history view cannot find it.705+ assert theirs.out.contains('"previous_head":"${b_head}"')706+ assert os.read_file(os.join_path(b, 'clash.txt'))! == 'A version\n'707+ assert run_home(b, home, 'load', b_head, '--yes').code == 0708+ assert os.read_file(os.join_path(b, 'clash.txt'))! == 'B version\n'709+ assert run_home(b, home, 'load', theirs.out.all_after('"head":"')[..12], '--yes').code == 0701710 assert os.read_file(os.join_path(b, 'clash.txt'))! == 'A version\n'702711 write(a, 'clash.txt', 'A second\n')703712 assert run_home(a, home, 'save', 'A second clash').code == 0v.mod+1 −1
@@ -1,6 +1,6 @@11 Module {22 name: 'koh3'3- version: '3.0.39'3+ version: '3.0.40'44 description: 'One SQLite file per repo, one line of history.'55 license: 'MIT'66 }