6c588d6233e4

refuse a folder remote that is not there instead of creating a stand-in at an unmounted disk's path (3.0.45)

time
2026-09-24 15:23 UTC · 2 weeks ago
machine
koh2
agent
claude-opus-5-5
session
koh 3 reliability campaign
parent
15b6b8d6c6d5
4 files VERSION+1 −1src/cli/cmd_offer.v+7 −0tests/cli_test.v+31 −0v.mod+1 −1
VERSION+1 −1
@@ -1 +1 @@1-3.0.441+3.0.45

view file

src/cli/cmd_offer.v+7 −0
@@ -3,6 +3,7 @@33 module cli44 55 import json6+import os67 import src.machine78 import src.remote89 import src.storage@@ -41,6 +42,12 @@4142 pub fn this_machine_backend() !remote.Backend {4243 	settings := machine.load_remote()!4344 	if settings.kind == machine.kind_folder {45+		// Only `koh remote` creates the folder. Missing later, it is usually a46+		// disk or share that is not mounted, and a stand-in folder would take47+		// saves the other machine never sees.48+		if !os.is_dir(settings.path) {49+			return error('the remote folder ${settings.path} is not there — is its disk mounted? ("koh remote" changes it)')50+		}4451 		return remote.new_folder(settings.path)!4552 	}4653 	credentials := machine.load_credentials()!

view file

tests/cli_test.v+31 −0
@@ -747,6 +747,37 @@747747 	assert text_clone.out.contains('[${id}] came in flagged — broke the build')748748 }749749 750+// A folder remote on a disk or share that is not mounted is simply not there.751+// Creating it would put the saves in a stand-in folder the other machine never752+// sees; the offer must say what is wrong instead.753+fn test_an_unmounted_folder_remote_is_not_recreated() {754+	syncroot := temp_repo('unmounted')755+	home := temp_repo('unmounted-home')756+	defer {757+		os.rmdir_all(syncroot) or {}758+		os.rmdir_all(home) or {}759+	}760+	mount := os.join_path(syncroot, 'nas')761+	remote := os.join_path(mount, 'koh')762+	a := os.join_path(syncroot, 'proj')763+	os.mkdir_all(a) or { panic(err) }764+	init_repo(a)765+	assert run_home(a, home, 'remote', 'folder', remote).code == 0766+	write(a, 'a.txt', 'one\n')767+	assert run_home(a, home, 'save', 'first').code == 0768+	os.rename(mount, os.join_path(syncroot, 'nas-unplugged')) or { panic(err) }769+	os.mkdir(mount) or { panic(err) }770+	offered := run_home(a, home, 'offer')771+	assert offered.code == 1772+	assert offered.err.contains('mounted')773+	assert !os.exists(remote)774+	write(a, 'a.txt', 'two\n')775+	saved := run_home(a, home, 'save', 'second')776+	assert saved.code == 0777+	assert saved.out.contains('mounted')778+	assert !os.exists(remote)779+}780+750781 fn test_man_and_koh2_handoff() {751782 	dir := temp_repo('man')752783 	home := temp_repo('man-home')

view file

v.mod+1 −1
@@ -1,6 +1,6 @@11 Module {22 	name: 'koh3'3-	version: '3.0.44'3+	version: '3.0.45'44 	description: 'One SQLite file per repo, one line of history.'55 	license: 'MIT'66 }

view file