Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
13 KiB
Final whole-branch review: tests, fake, harness, CI, docs, hygiene
Reviewer area: everything that is not production backend code. HEAD 29ad26944,
base 06700aaf2. Verified in a scratch worktree (removed afterwards).
Checks run
RCLONE_CONFIG=/notfound go test -race -count=1 ./backend/protondrive/...on 16 cores: PASS.backend/protondrive333.6 s,protontest38.3 s,protonfake1.4 s.- The same package pinned to 2 CPUs (
taskset -c 0-1,-timeout 60m) to approximate the slow Gitea runner: PASS in 420.7 s (70% of Go's default 10 min test timeout), no failures, timing assertions included. golangci-lint run ./backend/protondrive/...: 0 issues.go vet: clean.GOPROXY=off go mod tidy -diff: no diff. go.mod/go.sum: Proton-API-Bridge, go-proton-api, gluon, resty, goquery/cascadia, go-message/vcard, juniper, logrus, relvacode/iso8601 all gone; go-srp and gopenpgp/v3 promoted to direct. Nothing unexpected.TestIntegrationFake -v: 97 subtests pass, 29 skip (matches the handoff).- Test-account usernames (recovered from the pre-rewrite object
901d7fd5b, which is still in the local object store): 0 hits in HEAD's tracked files, text or binary (git grep -a). No tokens, UIDs or 32-hex IDs in committed docs/testdata. Largest tracked files outside upstream's MANUAL.* are the three golden CLI blocks (4 MiB, 4 MiB, 1 MiB), as intended. - shellcheck on
init.d/TestProtonDriveFake: SC2034 only (known minor). - Live-reach audit: every
NewFs/configmap.Simplein the tests carries the fake'sendpoint, except unit tests of the config store that never connect.TestIntegrationis gated only by the remote being configured (rclone's norm; hence the standingRCLONE_CONFIG=/notfoundrule) and skips during a golden capture;TestGoldenCaptureneeds all three-capture*flags;TestIntegrationFakeskips under-remote. No other test can reach Proton.
Critical
None.
Important
I-1. CI: go test -race has no -timeout; the package will hit Go's default 10 min on the slow runner
.gitea/workflows/ci.yml:43 runs go test -race ./backend/protondrive/...
with Go's default per-binary -timeout 10m. The backend/protondrive test
binary alone takes 333 s on 16 cores under -race; pinned to 2 of this host's fast cores it took 420.7 s, 70% of the 10 min budget, so a runner whose cores are ~1.5x slower (shared VM, older CPU, the build cache cold on top) crosses it. The
job's timeout-minutes: 45 never comes into play: the test binary panics with
"test timed out after 10m0s" first, and the job fails with a goroutine dump
rather than a result. The owner described the runner as "slow and not very
capable".
Fix: go test -race -timeout 40m ./backend/protondrive/... (inside the 45 min
job budget), or drop -race on CI and keep a non-race run (much faster) with a
nightly race job.
I-2. CI: go-version-file: go.mod installs Go 1.26.0, so govulncheck will very likely fail on standard-library advisories
ci.yml:22-27 takes the Go version from go.mod's go 1.26.0 directive (no
toolchain line). actions/setup-go treats that as an exact version and
installs 1.26.0, a release nine months older than the 1.27.4 every local
verification used. ci.yml:51 then runs govulncheck@latest against it;
govulncheck reports reachable standard-library vulnerabilities fixed in later
patch releases (net/http, crypto/tls, crypto/x509 are reached by this backend)
and exits non-zero. So the first push most likely goes red on a finding that is
about the CI toolchain, not the code; it also means CI tests on a compiler no
one has run the suite with. Could not be proven offline (needs vuln.go.dev).
Fix: go-version: '~1.27.3' (or stable), matching upstream's
.github/workflows/build.yml:290 lint job; pin govulncheck to a version
instead of @latest so a new govulncheck release can't break the build.
I-3. The committed workspace copy of the ledger stops at the 2026-11-11 pause; the handoff tells a new agent to restore from it
docs/plans/001-protondrive-tier1-HANDOFF.md:96-108 says a copy of every
workspace file "is committed at docs/plans/001-protondrive-tier1-workspace/"
and to resume with cp docs/plans/001-protondrive-tier1-workspace/*.md .superpowers/sdd/...; line 25 calls progress.md "the full execution ledger
(every ruling, every deferred minor, every live probe)". The committed
progress.md is 175 lines and ends "PAUSED 2026-11-11 ... Tasks 18–21 not
started"; the live ledger (gitignored .superpowers/) is 244 lines. Missing
from git: all Task 18–21 ledger entries (rulings on the lag fix and on
draft deletion, the live test_all results, the browser sign-in proof, the
config-create token incident), and 21 files: task-18/19/20/21/21f/21g
briefs, reports, reviews and re-reviews. On another machine, or after the
.superpowers/ directory is cleaned, a new agent loses the deferred minors of
Tasks 18–21 and the reasoning behind rulings the handoff only summarises.
Fix: re-copy .superpowers/sdd/001-protondrive-tier1/*.md into the workspace
directory (and do it again after this final review) and commit.
I-4. Handoff section 3 still describes the pre-Task-18 state
A new agent reading top-down is told things that are false now:
HANDOFF.md:3 "Written 2026-11-11 when development was paused"; :50 "Last
code commit: 9c90998f7" (it is be0053607 / f8e398fce); :76 verification
"at 9c90998f7"; :82-91 "Not done, and easy to assume otherwise": user docs
"still describe the old backend (Task 20)", wider suites "have never been run
against the new backend (Task 18)", "No fork CI exists (Task 18)" — all three
are done; :130 "Remaining tasks" lists 18–21 as remaining; section 8
(:463-466) says only Tasks 14, 16 and 17 have review files. The table at
:55-57 and section 6 are current, so the document contradicts itself.
Fix: rewrite section 3's header, commit references and "Not done" list (the real not-done list is: whole-branch review fix wave, 2FA/CAPTCHA/desktop clients live, root of trust on a Proton-made drive, the five cross-Fs tests), and mark the "Remaining tasks"/"Notes for Tasks 18 to 21" subsections as historical or delete them.
I-5. The upstream recipe is self-contradictory: the upstream branch would not compile its tests
Spec 13.4 (docs/specs/001-protondrive-tier1.md:729-732) says the upstream
branch contains backend/protondrive/ and the fstest/test_all/config.yaml
change; spec 14.4, the handoff (:2, :505-511) and ARCHITECTURE say
backend/protondrive/protontest never goes upstream. 13 of the 17 test files
import protontest (all but crypto_internal_test.go, cache_internal_test.go,
export_test.go, protondrive_test.go), so leaving it out makes
go test ./backend/protondrive/ fail to build upstream, and the
TestProtonDriveFake: entry in config.yaml:616-630 refers to an init.d
script and port that are also fork-only. Upstream is deferred, so this isn't
a defect today, but whoever cuts the branch per the written recipe produces a
broken PR, and the handoff (section 9) lists no remedy. Ties to open question
14.6.
Fix: record in the handoff section 9 and spec 13.4 that either protontest goes
upstream with the tests (and question 14.6 must be answered first), or the
upstream branch drops every protontest-dependent test file plus the
TestProtonDriveFake config.yaml entry, PORTS.md row and init.d script, and
keeps only protondrive_test.go and the two standalone internal tests.
Minor
- Timing assertions in parallel tests are tight for a slow
-racerunner.nodes_internal_test.go:503and:523assert a plainabout()call finishes in under 500 ms insidet.Parallel()tests that run alongside ~120 other parallel tests doing multi-MiB crypto under the race detector;:487-488(TestRetryAfterHonoured) asserts a 2 s Retry-After call under 5 s. They passed on the 2-CPU run, so this is a margin concern, not an observed flake. Fix: 2-5 s bounds, which still catch a held pacer (the cases guard against a 999999999 s or 60 s hold). - Goroutine-count leak checks remain (
download_internal_test.go:105-118,nodes_internal_test.go:582-596, known). They run in sequential tests, so false failures need a stray goroutine started by something else; the<= baselineform can also pass vacuously when goroutines of an earlier test exit during the window. Recorded flake ofTestUploadShortSourcein the handoff. Considergo.uber.org/goleak-style filtering is not available without a dependency; keep, but don't expand the pattern. TestListingRacingChangecan hang to the package timeout (nodes_internal_test.go:1222-1228, known, still present):<-heldhas no timeout andreleaseis closed only afterrequire.NoError(test.change); a failure leaves the fake's handler blocked andhttptest.Server.Closewaiting. Fix:t.Cleanup(func(){ close(release) })guarded by sync.Once, and aselectwithtime.Afteronheld.protonfakeTestRunleaks therungoroutine on a failed require (protontest/cmd/protonfake/main_test.go:17-34, known, still present). Fix:t.Cleanupclosingstop.TestClientBootstrapCreateVolumeRacewaits a fixed 2 s (nodes_internal_test.go:128-147, known, still present).TestUploadBlock502Oncedoesn't check the fault fired (upload_internal_test.go:230-237): a mistyped pattern would let it pass as a plain upload. Addassert.Equal(t, 2, srv.Requests()[storage route])or similar.TestAcquireTokenCancelsleeps 20 ms to "let the wait start" (upload_internal_test.go:479): on a slow runner the goroutine may not have started, so the cancelled-while-waiting path is not exercised (the test still passes via the early ctx check). Harmless, not a flake.- No test pins the 408 exclusion of
refusedCommit(21f re-review N3, still open). - Task 20 re-review nits still present:
download_internal_test.go:621names the unrecorded case "assumed";:178min(v.blocks, 1)*3is always 3. - init.d
stophas no SIGKILL escalation, SC2034 unusedi(fstest/testserver/init.d/TestProtonDriveFake:51-62, Task 18 minors, still present). - CI hygiene:
on: pushpluspull_requestwith noconcurrencygroup runs twice per PR push on a slow runner;govulncheck@latestis unpinned (see I-2). - The fake's read lag is never combined with the contract suite.
Server.SetReadLag(protontest/drive.go:1749-1757) models the live read-after-write lag that brokeTestPurge/vfs on the real server, but only targeted upload tests turn it on;TestIntegrationFakeandtest_allagainst the fake run with no lag, so the next lag-intolerant path (activeNode, recorded in TODO) can't show up offline. Consider a secondfstests.Run(or aprotonfake -read-lagflag fortest_all) with a lag of 1-2 reads. - Conformance checklist contradicts ruling 11:
docs/specs/001-conformance-checklist.md:27(upload row) says "a draft is deleted only on positive proof it never committed"; sincebe0053607the rule is "only when the server explicitly refused the commit, on a single unresent attempt; no read-back decides deletion". - User docs: the second "unknown" error isn't documented.
docs/content/protondrive.md:289-293names only "upload outcome unknown";errUploadUnconfirmed("upload committed, but the server doesn't show it yet",upload.go:43) gets the same no-retry treatment and is what a user will see under server lag. - User docs claim behaviour proven only against the fake:
CAPTCHA (
protondrive.md:35-37), 2FA prompting (:131-147) and "rclone aboutreports the whole account" (:548) are unverified live, whiledocs/TODO.mdsays the docs "avoid claiming" 2FA and CAPTCHA. Either soften or fix the TODO wording. - Lost TODO from the live run: the ledger's 2026-11-12 incident note
("
rclone config createprinted the new remote's client_* tokens and salted key pass to stdout ... check whether client_* options are marked Sensitive") is not indocs/TODO.md. The options areSensitive(protondrive.go:113-142), butconfig create/config showprint unredacted (fs/config/ui.goShowRemote) — generic rclone behaviour, yet worth a docs line ("the output ofrclone config createcontains the session; don't paste it") and a TODO entry, since the salted key password survives a logout. docs/PERMISSIONS.md/.codex/out of step:golangci-lint runwas added to the Allow tier and to.claude/and.gemini/, but not to.codex/rules/project.rules, against AGENTS.md's "change them together".- Old username-bearing commit still in the local object store
(
901d7fd5b, reachable from the reflog). Not tracked and not on the remote after the force-push; it disappears with reflog expiry/gc. Mention it in the handoff so agit push --mirroror a backup bundle of this checkout isn't made before then. - Handoff ruling 13 still reads as current ("stays in
go.moduntil Task 19"); mark it done (Task 19 review minor, still present).