Date: 2026-10-03. One reviewer (an AI agent), on c6cef99..a66625c: scd’s
checker goroutine (step 12), the netplan, udev, passwd, group and sysctl
checkers (step 13), sc scope (step 14), sc check --as and
scripts/accept-m3.sh. The reviewer read the code and did not run it.
Claude reproduced or tested each finding on this VM before fixing it.
| Area | Finding | Severity | Fixed in |
|---|---|---|---|
| passwd | passwd-root was a blocker (“sudo and su fail”) for a file with no root line, or one glibc cannot read. With passwd: files systemd in nsswitch, as on stock Ubuntu, nss-systemd supplies root and sudo keeps working. Reproduced on glibc 2.39 by bind-mounting a passwd file with no root line over /etc/passwd in a private mount namespace: getent passwd root gives uid 0 and sudo -n id -u prints 0. The acceptance script expected the blocker. |
high | 2fb037e: a warning when nsswitch lists systemd for passwd. A root line whose uid is not 0 is still a blocker. |
| netplan | When another netplan file could not be read (a user running sc check, a 0600 50-cloud-init.yaml), it was left out of the merge with a note, but an error caused by its absence (an interface “not defined”) was still a blocker in this file. |
high | 2fb037e: with the merge incomplete, an error that may come from a missing file (an interface not defined, already assigned, a device type change, a definition or writer error) is a note asking for root. Errors in the file’s own lines are still findings. |
| scd checker | A failed check of the version before stopped the whole check, so the new version was not checked; and on shutdown it logged a spurious error. | medium | 2fb037e: the failure is logged once at notice and every finding of the new version is reported; a cancelled context ends quietly. |
--as |
sc check --as PATH FIFO opened the FIFO before looking at it and waited for a writer forever. Reproduced. |
medium | 2fb037e: the candidate is opened with O_NONBLOCK and then refused as not a regular file. |
sc scope |
It treated every login’s .ssh as a root without the lstat check scd does, so it said “recorded: yes” for a path under a symlinked or missing .ssh that scd never watches. |
medium | 2fb037e: every root is looked at as scd does. Under a missing, symlinked or non-directory root a path is “recorded: no, scd does not watch …”. A root sc may not look at (/root/.ssh for a user) gives “yes if scd watches …; run sc scope as root”. |
| scd checker | Each change validated the version before again, though the last check had just validated it as its “after”. | low | 2fb037e: the findings of the last check are kept per path and blob and reused. Bounded like scd’s other per-path maps. |
| sysctl | The reviewer read the line matching to say key = and = 1 are invalid syntax for procps, so a later message could land on the wrong line. |
low | Not reproduced. procps 4.0.4 gives one cannot stat message per key = line, in order, and Path is not under /proc/sys for = 1. sc puts each on its own line (checked with sc check --as). No change. |
| code | Explain repeated decide’s walk and the tier and digest scans, so sc scope could drift from scd. |
cleanup | 2fb037e: decide returns the deciding line; Recorded and Explain both use it, and Tier and FingerprintOnly share one lookup. |
| code | Two lists of no-login shells (scope, check) and two PathError helpers (watch, check). |
cleanup | 2fb037e: scope.NoLogin and fsutil.ErrText. |
| code | sc check built a second, throwaway Checks just to read its graph, calling the test hook twice. |
cleanup | 2fb037e: one Checks per run, and Checks.GraphInUse replaces four copies of “the graph, or the default”. |
A go build ./cmd/sc at the repo root leaves an sc binary there, and a
git add -A took it into the fix commit before it was pushed. It was
taken out with an amend, and /sc is now in .gitignore.
TestPasswdRoot (with nss-systemd: no root line, an unreadable root
line, a non-zero uid; without it, or with it only for group: blockers), TestNetplanUnreadableMerge (five errors that become a
note, one that stays a blocker; disabling the rule fails all five),
TestCheckRunsOnce (two validator runs at the first check, one at the
next), TestCheckCLIAs (a FIFO is refused within 10 s; with the old open
it times out), TestScopeUnwatchedRoots (symlink, missing, a file, and a
home sc may not enter), TestCheckCLIOneChecks. TestScopeCLI no longer
depends on the machine’s /home. All tests pass as a user, as root and
under race.