Replace the seven duplicated 8-byte-hex random ID generators (interna... - #154
Merged
colonelpanik merged 1 commit intoAug 9, 2026
Conversation
colonelpanik
marked this pull request as ready for review
August 9, 2026 19:22
colonelpanik
force-pushed
the
overseer/replace-the-seven-duplicated-8-byte-hex-random-id-generators
branch
from
August 9, 2026 19:23
8ccd522 to
63340e7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Goal
Replace the seven duplicated 8-byte-hex random ID generators (internal/failover/coordinator.go:1679 newID, internal/grpcapi/host.go:934 newID, internal/grpcapi/users.go:436 generateID, internal/scheduler/rebalancer.go:709 newID, internal/grpcapi/notifications.go:91 newNotifyID, internal/health/owner_assert.go:335 ownerAssertID, internal/ui/handle_security_groups.go:173 newUIID) with one shared helper, picking a single explicit crypto/rand.Read error policy instead of today's mix of silent-discard (six sites) and propagate-error (newUIID, whose own comment says it 'matches the lv CLI's newID scheme').
Plan
Plan: one shared 8-byte-hex random ID helper
Replace the seven copy-pasted 8-byte-hex ID generators named in the task — plus
an eighth the task did not name,
cmd/litevirt/sg.go:244, for the reasons in§5 — with a single leaf-package helper, and settle the error policy that today is
split six-to-one.
Nothing about the generated ID changes: 8 bytes from
crypto/rand, hex-encoded,16 lowercase hex characters. Every one of these IDs is a primary key in a
replicated Corrosion table, so the shape is not ours to change (see
Reviewer notes).
1. The decision: the helper returns a bare
string, never an errorToday six sites discard the
crypto/rand.Readerror and one(
internal/ui/handle_security_groups.go:173newUIID) propagates it. The planpicks discard — structurally, by not having an error to return.
This is not a coin flip. Three independent reasons:
a. The Go 1.26 stdlib contract makes the error leg unreachable.
go.moddeclares
go 1.26.0, and in this toolchaincrypto/rand.Readis documented:(
$(go env GOROOT)/src/crypto/rand/rand.go:41-46.) The stdlib has already takenover failure handling, and it does so by killing the process. A caller-visible
errorreturn cannot fire. Propagating it isn't defence in depth; it is deadcode that reads like a live path.
b. The repo has already voted, twice.
internal/ui/handle_firewall.go:201adds a whole wrapper for the sole purpose of throwing the error away, and its
comment says so:
Four of the six
newUIIDcall sites already useid, _ :=. The error return hasproduced one wrapper and four ignored values, and zero handled failures.
c. The propagate policy is the more dangerous of the two here.
newUIIDreturns
("", err).mustUIIDdrops the error and returns"". That emptystring then goes in as a primary key —
corrosion.InsertFirewallRule(... ID: mustUIID() ...)(
internal/ui/handle_firewall.go:74,106,145). In an LWW-merged CRDT store everyrow that took that path collides on the same empty-string PK and silently merges
cluster-wide. The no-error helper cannot produce that state, because there is no
failure path to fall out of. Removing the error return closes a latent
fail-open bug rather than opening one.
The honest counter-argument, for the record: dropping an
errornormally losesinformation. It doesn't here — the stdlib made the promise and owns the failure —
and this is the same reasoning the stdlib itself used when it changed
Read'scontract.
math/randis nowhere in this change, so there is nosilently-weaker-randomness risk.
Implementation shape.
_, _ = rand.Read(b)with a comment citing thecontract. Explicitly not:
panicon error — unreachable, and it invites a reviewer to ask for a testthat cannot be written;
rand.Text()(Go 1.24+) — base32, 26 chars, would change the persisted format;github.com/google/uuid— excluded by the task; also longer and it wouldchange every persisted ID.
The
//nolint:errcheckmarkers on four of the current sites can go:_, _ =isan explicit discard that
errcheckaccepts. (This repo has no.golangci.ymland CI runs only
go vet ./..., so those markers are for contributors' locallinters, not a gate — but the shared helper should be lint-clean anyway.)
2. The new package:
internal/randidA stdlib-only leaf, modelled directly on
internal/safename, which exists forexactly this reason and says so in its own doc comment: "It is zero-dependency
(stdlib only) so any package can import it without risking an import cycle […]
This generalizes the per-package withinDir/safeJoin helpers." Same precedent,
same justification.
internal/failover,internal/grpcapi,internal/scheduler,internal/health,internal/ui, andcmd/litevirtcan all import it; itimports nothing from the repo, so no cycle is reachable even in principle.
Name: package
randid, functionNew().randid.New()reads cleanly at ~50call sites;
randid.NewID()stutters.internal/randid/randid.go(new)Not folded in, per the task and because each differs in size and purpose:
internal/daemon/daemon.go:1731generatePassword(16 bytes, admin password),internal/grpcapi/users.go:280newSessionID(32 bytes, session token),internal/pbsstore/chunkstore.go:477randomChunkSuffix(4 bytes, test-only),and the inline 32-byte API-token generation at
internal/grpcapi/users.go:386.3. Files to change
50 call-site rewrites — 48 in 20 production files, 2 in test files.
grepfinds 57 references to the seven generators underinternal/; the other7 are not rewritten but disappear: 1 is the
newUIID()call inside themustUIIDwrapper that this change deletes, and 6 live inside the foursuperseded tests (§3, test-files table). Counts verified in
Appendix A.
Mechanical, but the import bookkeeping differs per file, so it is spelled out.
Column meanings. Sites = calls to rewrite. Drop imports = whether the
generator was the only user of
crypto/randandencoding/hexin that file (ifyes, both imports must go or the build breaks on unused imports — verified per
file).
Definitions to delete
crypto/rand+encoding/hexinternal/failover/coordinator.gonewID(1678–1683)internal/grpcapi/host.gonewID(933–938)internal/grpcapi/users.gogenerateID(436–440)newSessionID:280 and the 32-byte token:386 still use bothinternal/grpcapi/notifications.gonewNotifyID(91–95)internal/scheduler/rebalancer.gonewID(708–713)internal/health/owner_assert.goownerAssertID(335–339)internal/ui/handle_security_groups.gonewUIID(172–179)internal/ui/handle_firewall.gomustUIID(201–206)mustUIIDdisappears with the error return that motivated it — its threecallers call
randid.New()directly.Call sites only (add the import, rewrite the calls)
internal/failover/coordinator.gointernal/grpcapi/host.gointernal/grpcapi/stacks.gointernal/grpcapi/firewall_rules.gointernal/grpcapi/reservation_admission.gointernal/grpcapi/lb.gointernal/grpcapi/migrate_container.gointernal/grpcapi/users.gointernal/grpcapi/notifications.gointernal/grpcapi/streamevents.gointernal/grpcapi/vm_events.gointernal/grpcapi/promote.gointernal/grpcapi/project_authority_admission.gointernal/grpcapi/registry_creds.gointernal/scheduler/rebalancer.gointernal/health/owner_assert.gointernal/health/container_owner_assert.gointernal/ui/handle_security_groups.gointernal/ui/handle_firewall.gointernal/ui/handle_notifications.goControl-flow changes (the only non-mechanical edits)
Two UI handlers currently branch on the unreachable error. Both branches are
deleted, not kept:
internal/ui/handle_security_groups.go:75-80(handleCreateSG)internal/ui/handle_security_groups.go:129-134(handleAddSGRule)Each is:
becoming
id := randid.New(). No test asserts the"id generation failed"toast (grepped: the string appears only in the two handlers), so no test
changes fall out of this. The remaining
err-shadowing in those functions isthe
corrosion.Insert*error, which stays checked — worth a careful readduring review that removing the earlier
err :=does not turn a laterif err := ...into a compile error. It does not: both later uses are their ownif err := corrosion.X(...); err != nilstatements.Test files
internal/grpcapi/host_test.goTestNewID_UniqueAndLength(347–356) — superseded by therandidtestsinternal/grpcapi/users_test.goTestGenerateID(190–199) — supersededinternal/grpcapi/grpcapi_coverage_test.goTestGenerateID_Unique(3673–3682) andTestNewID_Unique(3822–3831), and their now-empty─── generateID …/─── newID ───section bannersinternal/grpcapi/resize_admission_test.gonewID()→randid.New()(used as an op-ID argument)Four scattered uniqueness/length tests collapse into one authoritative set in
internal/randid. Coverage of the generator is strictly better after this(see §4); coverage of
grpcapidrops by four trivial tests, and CI has nocoverage gate (
.github/workflows/ci.ymlruns build, vet, test, guards only).Two comments name the old function and should be updated so they don't send a
future reader looking for a symbol that no longer exists:
internal/grpcapi/reservation_admission_test.go:102andinternal/grpcapi/overcommit_reservation_test.go:21(both say "ids come fromnewID()").4. Tests
4a.
internal/randid/randid_test.go(new)The format tests are the ones that matter — they are what stops a future edit
from changing a persisted ID shape.
TestNewDecodesToExactlyEightByteshex.DecodeString(New())succeeds and yieldslen == 8TestNewLengthAndAlphabetregexp ^[0-9a-f]{16}$over 100 drawsTestNewUniquemap[string]boolbcollapses every ID to0000000000000000), and amath/randswap with a fixed seedTestBytesConstantIsEightBytes == 8TestNewUniqueis the entropy test in practical terms: the realistic failuremode is not a biased generator, it is a generator that stopped generating.
4b.
internal/randid/drift_guard_test.go(new) — the part that makes it stickA dedup that isn't guarded drifts back. This repo already institutionalises
source-scanning guards (
scripts/ci/writecheck,scripts/ci/stmtshapecheck,cmd/litevirt/docs_triangulation_test.go), so the pattern is established.TestNoLocalEightByteHexIDGeneratorswalksinternal/,cmd/, andtests/from the module root — reusing the
repoRoot(t)walk-up-to-go.modidiom atcmd/litevirt/docs_triangulation_test.go:585— parses each production.gofile with
go/parser, and fails on any function body that contains all threeof
make([]byte, 8), arand.Readcall, andhex.EncodeToString. Files underinternal/randidare skipped.Deliberately narrow, so it needs no allowlist: it keys on the literal size
8, and the three generators the task excludes are 16, 32, and 4 bytes. The onlyother
make([]byte, 8)in the repo isinternal/qcow2/header.go:232(
writeEndOfExtensions), andinternal/qcow2imports neithercrypto/randnorencoding/hex, so it cannot match. No innocent code trips it.It lives as a normal
go testin the new package rather than a newscripts/ci/binary, sogo test ./...and CI pick it up with noMakefileorworkflow edit.
If a reviewer judges this scope creep, it can be dropped — but not
independently: dropping the guard is what makes the CLI migration optional
again. I recommend keeping both: the task is a drift fix, and the guard is the
only part that prevents the ninth and tenth copy.
4c. Mutation verification (required by
CLAUDE.md)Each mutation is applied, the named test confirmed red, then reverted:
Bytes = 4TestNewDecodesToExactlyEightBytes,TestNewLengthAndAlphabet,TestBytesConstantIsEightrand.Readline (leavebzeroed)TestNewUniquehex.EncodeToString→base64.StdEncoding.EncodeToStringTestNewLengthAndAlphabetnewID(the old body) back intointernal/health/owner_assert.goTestNoLocalEightByteHexIDGenerators"found no litevirt_* identifiers … scan path wrong?"self-check atdocs_triangulation_test.go:84That last one is the vacuous-test trap
CLAUDE.mdwarns about: a source-walkingguard that finds no files passes, and reads like a pass. The guard asserts a
non-zero file count for the same reason the docs guard does.
4d. Integration proof
The change is a refactor with no behavioural delta, so the existing suites are
the proof that call sites still work end to end. The multi-node paths that
consume these IDs — failover audit entries and relocation tokens, scheduler
proposals — are covered by
tests/fleet/(failover_test.go,failover_invariants_test.go,failover_dispute_test.go,audit_evidence_test.go), which CI runs. No new fleet test is warranted: nofailure mode introduced here is multi-node, because the ID bytes are unchanged.
4e. Full verification sequence
Then confirm the diff is format-neutral:
That last
grepis the cheap version of the §4b guard, and its expected outputis only correct because the CLI migration is in scope — if
cmd/litevirt/sg.gostill had its generator, this line and the guard would both disagree with the
execution plan. Running it by hand before writing the guard is the quickest way
to notice that class of contradiction.
make ci-guardsis expected to pass untouched: no schema change (nointernal/corrosion/schema.goedit, so noCurrentSchemaVersionbump), no SQLbuilder change (
stmtshapecheckunaffected), nocorrosion.<Fn>write-errorhandling change (
writecheckunaffected), and no new CLI command, config key, orlitevirt_*identifier (docs triangulation unaffected — it only checks thosethree things, so a new internal package needs no doc).
5. Reviewer notes
Things about this codebase that bear on whether the approach is right.
There is an eighth generator, and the task names seven.
cmd/litevirt/sg.go:244newID() (string, error)is byte-identical tonewUIID— and it is the origin of the scheme, sincenewUIID's comment saysit "matches the
lvCLI's newID scheme". Its own comment ("We use raw 8-byterandom rather than UUIDs to keep ids short in CLI output") is the best statement
in the repo of why the format is what it is, and worth preserving into
randid's doc comment.This is now a required step, and it widens the task's stated scope by one
file. Flagging that plainly rather than slipping it in. An earlier draft of
this plan marked it optional while also having the drift guard scan
cmd/—which was incoherent, since the guard flags this exact function, so step 2 could
never have gone green. Review round 4 caught it. Two ways out, and this is the
better one:
newID(243–250), dropcrypto/rand+encoding/hex(sole user);id := randid.New(), and each surroundingif err != nil { return err }collapses;cmd/litevirtispackage mainin the same module, so the import is fine.Beyond making the guard coherent, it is the right call on the merits: leaving it
converts the drift from 7 copies to 2 rather than to 0, keeps a comment
cross-referencing a scheme that no longer exists anywhere else, and leaves the
error policy split after a change whose entire purpose was to unify it. One of
the two surviving copies would be the one the other six were modelled on.
If the scope widening is vetoed — the task did say seven — the fallback is
coherent but weaker, and needs three specific corrections, not just dropping
step 3:
internal/only (dropcmd/andtests/);blind to
cmd/, where a known duplicate survives;// TODOatcmd/litevirt/sg.go:243recording that the generator is aknown duplicate the guard cannot see, so the next reader isn't misled by a
green test suite.
I'd rather not: a guard whose blind spot is precisely where the known duplicate
lives teaches the wrong lesson, and this repo's other guards
(
writecheck,stmtshapecheck) use explicit per-line opt-outs with statedreasons instead of silent directory exclusions.
internal/pkiis documented as not importable across modules, but this repo isa single module (one
go.mod, verified) — that warning is about externalconsumers and does not constrain
internal/randid.These IDs are CRDT primary keys, which is why format stability is a hard
constraint and not a style preference. They key
corrosionrows for auditentries, security groups and rules, firewall rules, IP sets, notification
targets and routes, reservations, LB generations, relocation tokens, and
scheduler proposals. Rows merge last-writer-wins on the PK, so an ID collision
does not error — two logically distinct rows silently become one. 8 bytes is 64
bits; the birthday bound puts collisions in the billions of rows per table.
This plan preserves that entropy exactly. It is also why
randid.Newmust staycrypto/rand-backed and must never be "optimised" tomath/rand: two of thecall sites (
internal/failover/coordinator.go:1363,1527) use the value as arelocation token, not just a name.
No injectable/seedable variant is included, on purpose. Nothing in the repo
stubs ID generation today — the four existing tests only check uniqueness and
length, and tests that need a deterministic ID already pass a literal. Adding a
package-level
var reader = rand.Readerseam now would be YAGNI and would addglobal mutable state to a leaf package. If a future test genuinely needs
determinism, the fix is to pass the ID in as an argument, which is already the
prevailing style (
s.admitResizeReservation(ctx, newID(), ...)).Why delete the seven wrappers rather than keep them as one-line
delegations. Keeping
func newID() string { return randid.New() }in eachpackage would be a ~10-line diff instead of a ~50-site one, and would
deduplicate the logic. It was rejected: it leaves seven names for one concept
(so the next contributor still can't tell whether
generateIDandnewNotifyIDdiffer), it leavesnewUIID's error return and thereforemustUIIDin place, and it doesn't unify the error policy at the call sites,which is half of the stated goal. The larger diff is entirely mechanical and
go buildcatches every miss.The
//nolint:errcheckmarkers are not load-bearing. There is no.golangci.ymlin this repo and CI runs onlygo vet ./.... Four sites carry//nolint:errcheckand two use_, _ =for the same purpose — itself a smallpiece of the same drift. All of it converges on one
_, _ =line.6. Execution order
Three commits, each independently reviewable and each leaving the tree green.
Conventional-commit style with package scopes, per
CLAUDE.md.The ordering below is a correction from an earlier draft, which shipped the
drift guard in commit 1 and left the CLI migration optional in commit 3. That
could not work: the guard scans
cmd/, so it stays red from commit 1 until theeighth generator is gone (§4b). The guard therefore lands last, when there is
nothing left for it to flag.
feat(randid): add shared 8-byte hex random ID helperinternal/randid/randid.go+randid_test.go(the §4a format and uniquenesstests only — not the guard). Self-contained and green: it adds a package
with tests and changes no caller.
refactor(randid): replace eight duplicated 8-byte hex ID generatorsThe seven named generators plus the CLI's (§5): 20 production files, 4 test
files, and
cmd/litevirt/sg.go. Delete the eight definitions plusmustUIID, rewrite the 50 sites (+2 insg.go), fix imports, delete the twoUI error branches and the four superseded tests, update the two stale
comments. Green:
go buildcatches any missed site. Run the full §4esequence here.
test(randid): guard against re-introducing local 8-byte hex ID generatorsThe §4b guard. Green on arrival, precisely because commit 2 left it nothing to
find — which is also why the guard's own red-to-green proof has to come from
the §4c mutation matrix (re-add a local generator, confirm red, revert) rather
than from commit ordering. Droppable only together with the
sg.gopart ofcommit 2 (§5).
Then the mutation matrix in §4c, since a green suite here is otherwise
indistinguishable from a suite that stopped testing anything.
If the §5 scope veto applies, commit 2 excludes
cmd/litevirt/sg.goand commit3's guard walks
internal/only — with the two claim corrections and theTODOlisted there.
Appendix A: verification log
Added after review round 1, in which the reviewer's sandbox could not read the
workspace at all (see Appendix B) and so
could not check any claim above. Every load-bearing factual claim in this plan,
with the command that establishes it and what it returned in this worktree at
HEAD=de655ea. A reviewer with filesystem access should re-run these ratherthan trust the table; a reviewer still without access at least has an auditable
trail.
grep -n "func newID|func generateID|func newNotifyID|func ownerAssertID|func newUIID" <the 7 files>//nolint:errcheckdiscard ×4 (coordinator, host, users, owner_assert);_, _ =discard ×2 (rebalancer, notifications);return "", err×1 (newUIID)crypto/rand.Readcannot failgo doc crypto/rand Read;$(go env GOROOT)/src/crypto/rand/rand.go:41-46go version;head go.modgo1.26.0 linux/amd64;go 1.26.0find . -name go.mod -not -path "./.git/*"./go.mod//nolintmarkers aren't load-bearingfind . -name ".golangci*";grep -n "lint|errcheck|vet" .github/workflows/ci.ymlrun: go vet ./...mustUIIDexists purely to discard the error, 3 call sitesgrep -rn "mustUIID"grep -rn "\bnewID(|\bgenerateID(|\bnewNotifyID(|\bownerAssertID(|\bnewUIID(|\bmustUIID(" --include="*.go" internal/ | grep -v ":func |//"mustUIID), 8 in test files, 20 distinct production filesgrep -rn "make(\[\]byte, 8)" --include="*.go" .internal/qcow2/header.go:232only; qcow2 imports neithercrypto/randnorencoding/hex(grep -n "crypto/rand|encoding/hex" internal/qcow2/*.go→ no production hit)grep -A2 "func generatePassword|func newSessionID|func randomChunkSuffix"grep -n "^func TestGenerateID\b|^func TestNewID_UniqueAndLength|^func TestGenerateID_Unique|^func TestNewID_Unique" internal/grpcapi/*_test.gointernal/safenameis the stated precedenthead -20 internal/safename/safename.gorepoRootwalk-up idiom exists to copysed -n '585,605p' cmd/litevirt/docs_triangulation_test.gorepoRoot(t)walks up togo.mod,t.Fatalif absentdocs_triangulation_test.golitevirt_*string literalswritecheckcan't be trippedhead -40 scripts/ci/writecheck/main.gocorrosion.<Fn>writerssed -n '243,250p' cmd/litevirt/sg.gofunc newID() (string, error), comment: "raw 8-byte random rather than UUIDs to keep ids short in CLI output"grep -rln "id generation failed" internal/ui/_test.gohitsed -n '243,251p' cmd/litevirt/sg.gomake([]byte, 8),rand.Read(b),hex.EncodeToString(b)— all present innewIDTwo things this log does not establish, because they can only be checked
after the code is written: that
go build ./... && go vet ./... && go test ./...stay green, and that the §4c mutations actually turn the new tests red. Those are
implementation-time obligations, not plan claims.
Corrections this plan has been through, recorded so a reviewer can see what
moved rather than diffing drafts:
call sites." Recounting produced 57 references / 50 rewrites. The per-file
tables in §3 were correct — only the summary figure was wrong.
cmd/while the CLI migration was marked optional, so the guard could never have gone
green at the end of commit 2 as §6 claimed. Resolved by making the CLI
migration required (§5), moving the guard to the last commit (§6), and
restating §4b's no-allowlist claim in terms of innocent code rather than
implying nothing matches. This was a real internal contradiction, not a
presentation issue: following the old §6 literally would have left CI red.
Appendix B: reviewer sandbox failure (rounds 1–3)
Three consecutive review rounds returned the same single finding — escalating
from major to critical — that PLAN.md and the referenced source files could
not be read because the sandbox launcher could not create its namespace, so the
plan could not be validated.
I don't think this is a defect in the plan, and I've changed nothing in
response to it beyond adding Appendix A. It reports that the reviewer's
sandbox could not start, not that anything in the plan is wrong. There is no
edit to PLAN.md that would fix it. Recording the diagnosis here since I could
reproduce it and the next reviewer will likely hit the same wall.
I agree with the finding's conclusion, though, and want to be unambiguous
about it: do not treat this plan as reviewed. Three failed rounds are three
non-reviews, not tacit approval, and Appendix A is self-verification, which is
not independent verification. The plan should not be implemented on the strength
of these rounds.
The artifact is present and readable.
PLAN.md, mode-rw-rw-r--, ownerkal:kal, in the worktree root at/home/kal/.overseer/worktrees/replace-the-seven-duplicated-8-byte-hex-random-id-generators/PLAN.md.My own tools read it and the whole repo without trouble in this same session.
Reproduced, with a root cause.
bwrap --dev-bind / / --unshare-pid truefails here with "No permissions to create a new namespace, likely because the
kernel does not allow non-privileged user namespaces." The usual suspects are
not the cause —
/proc/sys/kernel/unprivileged_userns_cloneis1and/proc/sys/user/max_user_namespacesis2147483647. The cause is:On this kernel (
Linux 7.0.0-28-generic) that AppArmor setting blocksunprivileged user-namespace creation for unconfined binaries, which is exactly
what
bwrap0.11.1 needs. Resolution is environmental and needs root, so it isthe operator's call, not something I should change unasked — a system-wide
sandboxing sysctl is not mine to flip. The options, in order of preference:
access, and the finding says reads are what failed.
/usr/bin/bwrapgrantinguserns create(the packaged fix on distros that ship this restriction).
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0— effectivebut it lowers a host-wide security boundary; least preferred.
Re-checked at round 3:
bwrapfails identically and the sysctl is still1.Nothing has changed, so nothing I can do from inside the workspace will change
the outcome of round 4 either.
The one alternative hypothesis is now ruled out.
PLAN.mdis gitignored(
.gitignore:71:/PLAN.md, confirmed viagit check-ignore -v PLAN.md), andreview tooling that enumerates tracked files would miss it for that reason
alone. But rounds 2 and 3 report that the referenced source files were
unreadable too, and those are tracked —
git ls-files --error-unmatch internal/failover/coordinator.gosucceeds. A gitignore rule cannot hide atracked file. So the failure is the namespace error at the read layer, affecting
every path equally, and
git add -f PLAN.mdwould not help. I have thereforenot done it: it would not fix the finding, and committing an artifact the task
scoped as the sole deliverable is the operator's decision, not mine.
What I am asking for, since I cannot do it myself. Every remedy needs root
on the host. Preference order is above; option 1 (run the reviewer without its
sandbox — it only needs read access, and reads are precisely what failed) is
both the safest and the most likely to be a one-line config change on the review
harness. Option 3 flips a host-wide AppArmor boundary and I would not run it
unasked even if I could.
Meanwhile, the substance is reviewable without filesystem access. This plan
quotes inline every source excerpt its argument depends on — the
crypto/randcontract (§1a), the
mustUIIDcomment (§1b), thesafenameprecedent (§2), theUI error branch being deleted (§3) — and Appendix A pairs each factual claim
with the command that establishes it. A reviewer who still cannot read the tree
can judge the design decisions from the plan text alone, and flag any claim in
Appendix A they want re-run once reads work.
Appendix C: implementation log — deviations from the plan
Written after carrying the plan out. The plan was accurate: every line number,
site count, import-drop verdict, and Appendix A claim I re-checked held at
HEAD=de655ea. Four things differ from what §3–§6 specify.1. One working tree, not three commits (§6). The implementation task
explicitly said not to commit. All three of §6's steps are present as a single
uncommitted change. The intermediate green states §6 relied on were still
verified in order:
internal/randidbuilt and tested green before any callermoved, and the drift guard was written only after the eighth generator was
gone, so it has never been red on arrival.
2. A third stale comment, not the two §3 names.
internal/corrosion/action_proofs.go:217read "A relocation token issingle-mint (
newIDper relocation)" — a reference tofailover.newID, whichthis change deletes. §3 lists only
reservation_admission_test.go:102andovercommit_reservation_test.go:21. Updated all three to sayrandid.New(),for the reason §3 gives for the other two: don't send a future reader looking
for a symbol that no longer exists.
internal/corrosion/vms.go:959also matchesa
newIDgrep but is an unrelated local variable, left alone.Consequence for §3's file count: 22 production files change, not the 20 §3
tables list plus
cmd/litevirt/sg.go. The extra one is this comment.3. The guard's "scan path wrong?" self-check is stricter than §4c asked
for. §4c's fifth mutation only requires that pointing the scan at a
nonexistent subdirectory fails loudly rather than passing on zero files. The
guard does that two ways: an
os.Statper scan root thatt.Fatals by name,and the
scanned == 0assertion §4b specified. Theos.Statleg is the onethat fires for a mistyped directory; the count leg still covers a root that
exists but yields no
.gofiles. Both are cheap and they fail for differentreasons, so I kept both. Mutation 5 was verified against the
os.Statleg.4. Pre-existing
gofmtdrift left alone.gofmt -l internal/grpcapi/reports eight files (
dualrun.go,move.go, and six_test.go). All eight areuntouched by this change — confirmed with
git diff --quiet HEAD -- <file>oneach — so the drift predates it and reformatting them would be unrelated noise
in this diff. Every file this change does touch is
gofmt-clean.Verification actually run
go build ./... && go vet ./...go test ./...tests/fleetandcmd/litevirtmake ci-guardsgrep -rn "make(\[\]byte, 8)"internal/qcow2/header.go:232plus three occurrences inside the guard's own comments — no generator survives anywhereAll five §4c mutations were applied, confirmed red, and reverted:
Bytes = 4decodes to 4 bytes, want 8;"74ec0286"vs the 16-char regexp;Bytes = 4, want 8)rand.ReadlineTestNewUnique:collision after 1 draws: "0000000000000000"hex→base64.StdEncodingTestNewLengthAndAlphabet:New() = "qaPfQVkehWM="ownerAssertIDback intointernal/health/owner_assert.goTestNoLocalEightByteHexIDGenerators:internal/health/owner_assert.go:336 func ownerAssertIDinternal-typoscan dir …/internal-typo missing: … — scan path wrong?Nothing in the plan turned out to be wrong about the code.
Final Codex review
No blocking findings remained.
Opened by overseer. The plan and the code each converged to zero blocking findings from Codex before this pull request was created.