29a1ceda1bfd
drop the forwarding kepr_host wrapper, the nil backend, the unused ask hook and the duplicated strip filter from steal and offer (3.0.105)
- time
- 2026-09-28 04:55 UTC · 2 weeks ago
- machine
- Junes-MacBook-Pro.local
- agent
- claude-opus-5-5
- session
- fix a2 review findings
- parent
- 934062214af8
↓ koh-29a1ceda.tar.gzatlas at this save
files changed8 files
VERSION+1 −1src/cli/cmd_remote.v+1 −7src/cli/cmd_steal.v+14 −18src/exchange/exchange.v+15 −16src/exchange/exchange_test.v+4 −2src/exchange/steal.v+1 −6src/remote/web.v+2 −0v.mod+1 −1src/cli/cmd_remote.v+1 −7
@@ -139,17 +139,11 @@139139 }140140 }141141 142-// kepr_host normalises a Kepr address: https unless the scheme says otherwise, and scheme, host143-// and port only. A path, a query, a fragment or credentials would send the code somewhere else.144-fn kepr_host(value string) !string {145- return remote.kepr_host(value)146-}147-148142 fn set_kepr_remote(args Args) int {149143 if args.positional.len != 1 {150144 return usage_error('koh remote <host> accepts one Kepr host')151145 }152- host := kepr_host(args.positional_at(0)) or { return refuse(args.json(), err.msg()) }146+ host := remote.kepr_host(args.positional_at(0)) or { return refuse(args.json(), err.msg()) }153147 connect_url := '${host}/connect?machine=${urllib.query_escape(machine.name())}'154148 println('Open ${connect_url} and sign in')155149 mut code := args.value('code')src/cli/cmd_steal.v+14 −18
@@ -57,12 +57,12 @@5757 // A stolen repo has its own remote: the address it came from, until this machine signs in to5858 // that Kepr, when the bucket takes over.5959 source := exchange.source_of(store)60- mut upgraded := source != '' && exchange.signed_in_to(exchange.source_host(source))61- mut backend := remote.Backend(unsafe { nil })62- if source != '' && !upgraded {63- backend = exchange.read_only(source) or { return refuse(args.json(), err.msg()) }60+ upgraded := source != '' && exchange.signed_in_to(exchange.source_host(source))61+ read_only := source != '' && !upgraded62+ mut backend := if read_only {63+ exchange.read_only(source) or { return refuse(args.json(), err.msg()) }6464 } else {65- backend = this_machine_backend() or { return refuse(args.json(), err.msg()) }65+ this_machine_backend() or { return refuse(args.json(), err.msg()) }6666 }6767 mut options := exchange.StealOptions{6868 mine: args.flag('mine')@@ -81,7 +81,7 @@8181 }8282 // The remote should end up with whatever this machine just decided — unless there is no remote8383 // to write to: a repo still riding its source stays read-only, and quietly so.84- offered := if source != '' && !upgraded { true } else { offer_after_steal(store, mut backend) }84+ offer_failed := !read_only && !offer_after_steal(store, mut backend)8585 if args.json() {8686 emit_json(json2.encode(StolenJson{8787 repo: report.repo@@ -93,7 +93,7 @@9393 replayed: report.replayed9494 kept_mine: report.kept_mine9595 head: report.head96- offer_failed: !offered96+ offer_failed: offer_failed9797 flagged: report.flagged.map(it.save_id)9898 previous_head: report.previous_head9999 kept_versions: report.kept_versions@@ -111,18 +111,14 @@111111 ok('brought in ${report.fetched_saves} saves from "${report.repo}" — head is now [${report.head}]')112112 }113113 } else {114- ok(if guided {115- if source != '' && !upgraded {116- 'combined'117- } else if offered {118- 'combined · offered to ${report.repo}'119- } else {120- 'combined'121- }122- } else {114+ ok(if !guided {123115 'brought in ${report.fetched_saves} saves from "${report.repo}"'116+ } else if read_only || offer_failed {117+ 'combined'118+ } else {119+ 'combined · offered to ${report.repo}'124120 })125- if source != '' && !upgraded {121+ if read_only {126122 note('your saves stay on this machine (read-only source)')127123 } else if !guided {128124 note("replayed ${report.replayed} of this machine's saves onto the remote head")@@ -141,7 +137,7 @@141137 if report.clashes.len > 0 {142138 note('${report.clashes.len} changed on both machines: ${report.clashes.join(', ')}')143139 }144- if !offered {140+ if offer_failed {145141 warn('the remote was not updated — run "koh offer" when it is reachable again')146142 }147143 return 0src/exchange/exchange.v+15 −16
@@ -290,14 +290,7 @@290290 }291291 }292292 for record in fetched.saves {293- if history.by_id(store, record.id) != none {294- continue295- }296- files := to_files(record).filter(fn [strips, record] (file snapshot.FileEntry) bool {297- return !strips.any(reaches(it, record.id, record.created_at)298- && covers(it.path, file.path))299- })300- history.import_save(store, to_save(record), files)!293+ import_arriving(store, record, strips)!301294 }302295 if strips.len > 0 {303296 store.drop_unreferenced_objects()!@@ -310,16 +303,22 @@310303 for hash, blob in fetched.repo_objects {311304 store.put_object_blob(hash, blob)!312305 }306+ strips := fetched.events_out.filter(it.kind == events.kind_strip)313307 for record in fetched.saves {314- if history.by_id(store, record.id) == none {315- strips := fetched.events_out.filter(it.kind == events.kind_strip)316- files := to_files(record).filter(fn [strips, record] (file snapshot.FileEntry) bool {317- return !strips.any(reaches(it, record.id, record.created_at)318- && covers(it.path, file.path))319- })320- history.import_save(store, to_save(record), files)!321- }308+ import_arriving(store, record, strips)!322309 }310+}311+312+// import_arriving lands one save from the remote, unless it is already here, less every path a313+// strip that `reaches` it removed.314+fn import_arriving(store &storage.Store, record SaveRecord, strips []events.Event) ! {315+ if history.by_id(store, record.id) != none {316+ return317+ }318+ files := to_files(record).filter(fn [strips, record] (file snapshot.FileEntry) bool {319+ return !strips.any(reaches(it, record.id, record.created_at) && covers(it.path, file.path))320+ })321+ history.import_save(store, to_save(record), files)!323322 }324323 325324 // reaches reports whether a strip event applies to a save arriving from thesrc/exchange/exchange_test.v+4 −2
@@ -405,7 +405,7 @@405405 root := b.root406406 // An agent saves while the person is still choosing.407407 if _ := steal(b, mut backend, StealOptions{408- ask: fn [root] (clash Clash) Side {408+ guide: fn [root] (preview StealPreview) !map[string]Side {409409 other := storage.open(root) or { panic(err) }410410 defer {411411 other.close()@@ -414,7 +414,9 @@414414 history.save_worktree(other, root, snapshot.load(root), history.SaveInput{415415 message: 'agent saves during the question'416416 }) or { panic('steal held the write lock while asking: ${err}') }417- return .mine417+ return {418+ preview.clashes[0].path: Side.mine419+ }418420 }419421 })420422 {src/exchange/steal.v+1 −6
@@ -57,8 +57,7 @@5757 pub mut:5858 mine bool // resolve every clash as mine5959 theirs bool // resolve every clash as theirs60- // Asked once per clash when neither flag is set. Nil means "nobody to ask".61- ask fn (clash Clash) Side = unsafe { nil }60+ // Shown the whole combine and asked about every clash. Nil means "nobody to ask".6261 guide fn (preview StealPreview) !map[string]Side = unsafe { nil }6362 yes bool6463 }@@ -515,10 +514,6 @@515514 }516515 if opts.theirs {517516 sides[clash.path] = .theirs518- continue519- }520- if opts.ask != unsafe { nil } {521- sides[clash.path] = opts.ask(clash)522517 continue523518 }524519 return ClashError{src/remote/web.v+2 −0
@@ -13,6 +13,8 @@1313 return Web{ base: kepr_host(base)! }1414 }1515 16+/// kepr_host normalises a Kepr address: https unless the scheme says otherwise, and scheme, host17+/// and port only. A path, a query, a fragment or credentials would send the code somewhere else.1618 pub fn kepr_host(value string) !string {1719 for letter in value {1820 if letter <= ` ` {v.mod+1 −1
@@ -1,6 +1,6 @@11 Module {22 name: 'koh3'3- version: '3.0.104'3+ version: '3.0.105'44 description: 'One SQLite file per repo, one line of history.'55 license: 'MIT'66 }