SmartConfig

Review of M3 chunk B (steps 5-7)

Date: 2026-10-03. One reviewer (an AI agent), on 1c616b6..d3032b2: sc check, Store.Replace with the restore refactor, and sc edit. The first three findings were reproduced with the built binary in a throwaway SC_HOME; the rest come from reading the code. Claude reproduced or confirmed each before fixing it.

Held up: the restore refactor (commitRestore into commitWrite, safeDir into safeDirFor) keeps its behaviour and removes no guard.

Findings

Area Finding Severity Fixed in
sc edit Any failure after the editor had run, other than “changed on disk”, deleted the edited copy: a locked store, a full disk, a failed check. Reproduced with an unwritable objects/. high a946f6f (the copy is kept and its path printed)
sc edit A SIGINT or SIGQUIT while the editor ran (Ctrl-C in a shell started from vim, EDITOR=ed) killed sc at once; the editor kept running and its work went to a copy the next sc edit deleted. Reproduced. high a946f6f (left to the editor, as sudoedit and git do; leftovers are kept, not deleted)
store For a new file, the absent state was recorded only when the path had no rows. After an unrecorded deletion the edit row followed a stale file row, so the creation could not be undone. Reproduced. medium a946f6f (a deleted row first)
sc check With no argument, a file sc could not read was a note and the exit status stayed 0: sc check && deploy as a user would pass over an unread sudoers. medium a946f6f (exit 1, one line on stderr)
sc check An id had to be 4 to 64 hex digits, while sc cat takes 1 to 6 in any case: deadbeef and a file named cafe failed oddly, a 3-digit prefix was taken for a path. medium a946f6f (an id when it looks like one and no such file exists)
sc edit The file was not required to hold still while first read; a torn copy would only be refused at the save. low a946f6f (3 tries)
sc edit The old content was checked before the editor opened: a wasted validator run for unchanged edits, and a stale baseline after a long edit. low a946f6f (checked after the editor, with the new content)
store Replace could write a file larger than sc reads back (8 MB). low a946f6f
check Graph.Files walked whole subtrees for patterns that match one level. low a946f6f
output The summary line of sc check and the last line of sc edit were wider than 80 columns. low a946f6f (two lines)

Tests

TestEditKeepsWorkWhenSaveFails, TestEditLeavesCtrlCToTheEditor (the real binary; a mutation that does not leave the signals to the editor fails it), TestEditValidatorRuns, the end-of-input and leftover cases of TestEditBlocker and TestEditFileChangedMeanwhile, TestCheckCLIUncheckedFile, the id cases of TestCheckCLIByID, and the unrecorded-deletion and size cases of the Replace tests. The retry of an unstable first read has no test of its own.

Seen while checking

TestWatchSIGHUPRescans (M2) timed out once in a root run under load: it allows 10 s for a rescan that comes about 10 s after the start. It passed 8 of 8 alone and in a full rerun. It joins the timing-bound tests to make robust before chunk D.