e6da6d4397fc

report a clone's flagged saves with their reasons in text and JSON, closing 3.0.20's codex review; drop the flagged_ids helper this orphaned (3.0.35)

time
2026-09-24 03:52 UTC · 2 weeks ago
machine
koh2
agent
claude-opus-5-5
session
koh 3 reliability campaign
parent
88ecb8e00ef9
6 files VERSION+1 −1src/cli/cmd_steal.v+18 −10src/events/events.v+0 −13src/events/events_test.v+0 −2tests/cli_test.v+28 −0v.mod+1 −1
VERSION+1 −1
@@ -1 +1 @@1-3.0.341+3.0.35

view file

src/cli/cmd_steal.v+18 −10
@@ -77,13 +77,7 @@7777 			note('kept mine for ${fmt.plural(report.kept_mine, 'file')}')7878 		}7979 	}80-	for flag in report.flagged {81-		warn('[${flag.save_id}] came in flagged${if flag.reason != '' {82-			' — ${flag.reason}'83-		} else {84-			''85-		}}')86-	}80+	warn_flagged(report.flagged)8781 	if report.clashes.len > 0 {8882 		note('${report.clashes.len} changed on both machines: ${report.clashes.join(', ')}')8983 	}@@ -175,6 +169,13 @@175169 	snapshot.restore_files(store, target, snapshot.Ignore{}, files, []) or {176170 		return refuse(args.json(), err.msg())177171 	}172+	mut flagged := []events.Flag{}173+	for _, flag in events.latest_flags(store) or { return refuse(args.json(), err.msg()) } {174+		if flag.flagged {175+			flagged << flag176+		}177+	}178+	flagged.sort(a.save_id < b.save_id)178179 	if args.json() {179180 		emit_json(json.encode(StolenJson{180181 			repo:    name@@ -183,15 +184,22 @@183184 			objects: fetched.repo_objects.len184185 			events:  fetched.events_out.len185186 			head:    fetched.head187+			flagged: flagged.map(it.save_id)186188 		}))187189 		return 0188190 	}189191 	ok('fetched "${name}" into ${target}')190192 	note('${fetched.saves.len} saves at [${fetched.head}]')191-	for save_id in events.flagged_ids(store) or { []string{} } {192-		warn('[${save_id}] came in flagged')193-	}193+	warn_flagged(flagged)194194 	return 0195+}196+197+// warn_flagged prints the line the document asks for, per incoming flagged save.198+fn warn_flagged(flags []events.Flag) {199+	for flag in flags {200+		reason := if flag.reason != '' { ' — ${flag.reason}' } else { '' }201+		warn('[${flag.save_id}] came in flagged${reason}')202+	}195203 }196204 197205 struct ReposJson {

view file

src/events/events.v+0 −13
@@ -206,19 +206,6 @@206206 	}207207 }208208 209-// flagged_ids is what the log and the history view mark.210-pub fn flagged_ids(store &storage.Store) ![]string {211-	flags := latest_flags(store)!212-	mut ids := []string{}213-	for save_id, state in flags {214-		if state.flagged {215-			ids << save_id216-		}217-	}218-	ids.sort()219-	return ids220-}221-222209 pub fn strip_events(store &storage.Store) ![]Event {223210 	mut stmt := store.db.prepare('${select_event} WHERE kind = ? ORDER BY created_at, id')!224211 	defer {

view file

src/events/events_test.v+0 −2
@@ -30,11 +30,9 @@3030 	state := flag_state(store, save.id)!3131 	assert state.flagged3232 	assert state.reason == 'broken build'33-	assert flagged_ids(store)! == [save.id]3433 3534 	clear_flag(store, save.id)!3635 	assert !flag_state(store, save.id)!.flagged37-	assert flagged_ids(store)!.len == 03836 3937 	// Clearing is itself an event, so flagging stays undoable: the events are4038 	// all still there, newest simply wins — even when both land in the same

view file

tests/cli_test.v+28 −0
@@ -701,6 +701,34 @@701701 	assert run_home(b, home, 'log').out.contains('combine: kept mine for')702702 }703703 704+// A clone brings in the whole history, flags included; both output forms must705+// say which saves came in flagged, and why.706+fn test_a_clone_reports_flagged_saves() {707+	syncroot := temp_repo('clone-flags')708+	home := temp_repo('clone-flags-home')709+	defer {710+		os.rmdir_all(syncroot) or {}711+		os.rmdir_all(home) or {}712+	}713+	a := os.join_path(syncroot, 'A', 'proj')714+	os.mkdir_all(a) or { panic(err) }715+	init_repo(a)716+	assert run_home(a, home, 'remote', 'folder', os.join_path(syncroot, 'remote')).code == 0717+	write(a, 'a.txt', 'one\n')718+	id := run_home(a, home, 'save', 'risky', '--json').out.all_after('"id":"')[..12]719+	assert run_home(a, home, 'flag', id, '--reason', 'broke the build').code == 0720+	assert run_home(a, home, 'offer').code == 0721+	for name in ['B', 'C'] {722+		os.mkdir_all(os.join_path(syncroot, name)) or { panic(err) }723+	}724+	json_clone := run_home(os.join_path(syncroot, 'B'), home, 'steal', 'proj', '--json')725+	assert json_clone.code == 0726+	assert json_clone.out.contains('"flagged":["${id}"]')727+	text_clone := run_home(os.join_path(syncroot, 'C'), home, 'steal', 'proj')728+	assert text_clone.code == 0729+	assert text_clone.out.contains('[${id}] came in flagged — broke the build')730+}731+704732 fn test_man_and_koh2_handoff() {705733 	dir := temp_repo('man')706734 	home := temp_repo('man-home')

view file

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

view file