Files
rclone/docs/plans/001-protondrive-tier1-workspace/final-review-tests.md
T
2026-11-12 15:12:51 +01:00

13 KiB
Raw Blame History

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/protondrive 333.6 s, protontest 38.3 s, protonfake 1.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.Simple in the tests carries the fake's endpoint, except unit tests of the config store that never connect. TestIntegration is gated only by the remote being configured (rclone's norm; hence the standing RCLONE_CONFIG=/notfound rule) and skips during a golden capture; TestGoldenCapture needs all three -capture* flags; TestIntegrationFake skips 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

  1. Timing assertions in parallel tests are tight for a slow -race runner. nodes_internal_test.go:503 and :523 assert a plain about() call finishes in under 500 ms inside t.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).
  2. 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 <= baseline form can also pass vacuously when goroutines of an earlier test exit during the window. Recorded flake of TestUploadShortSource in the handoff. Consider go.uber.org/goleak-style filtering is not available without a dependency; keep, but don't expand the pattern.
  3. TestListingRacingChange can hang to the package timeout (nodes_internal_test.go:1222-1228, known, still present): <-held has no timeout and release is closed only after require.NoError(test.change); a failure leaves the fake's handler blocked and httptest.Server.Close waiting. Fix: t.Cleanup(func(){ close(release) }) guarded by sync.Once, and a select with time.After on held.
  4. protonfake TestRun leaks the run goroutine on a failed require (protontest/cmd/protonfake/main_test.go:17-34, known, still present). Fix: t.Cleanup closing stop.
  5. TestClientBootstrapCreateVolumeRace waits a fixed 2 s (nodes_internal_test.go:128-147, known, still present).
  6. TestUploadBlock502Once doesn't check the fault fired (upload_internal_test.go:230-237): a mistyped pattern would let it pass as a plain upload. Add assert.Equal(t, 2, srv.Requests()[storage route]) or similar.
  7. TestAcquireTokenCancel sleeps 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.
  8. No test pins the 408 exclusion of refusedCommit (21f re-review N3, still open).
  9. Task 20 re-review nits still present: download_internal_test.go:621 names the unrecorded case "assumed"; :178 min(v.blocks, 1)*3 is always 3.
  10. init.d stop has no SIGKILL escalation, SC2034 unused i (fstest/testserver/init.d/TestProtonDriveFake:51-62, Task 18 minors, still present).
  11. CI hygiene: on: push plus pull_request with no concurrency group runs twice per PR push on a slow runner; govulncheck@latest is unpinned (see I-2).
  12. 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 broke TestPurge/vfs on the real server, but only targeted upload tests turn it on; TestIntegrationFake and test_all against the fake run with no lag, so the next lag-intolerant path (activeNode, recorded in TODO) can't show up offline. Consider a second fstests.Run (or a protonfake -read-lag flag for test_all) with a lag of 1-2 reads.
  13. 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"; since be0053607 the rule is "only when the server explicitly refused the commit, on a single unresent attempt; no read-back decides deletion".
  14. User docs: the second "unknown" error isn't documented. docs/content/protondrive.md:289-293 names 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.
  15. User docs claim behaviour proven only against the fake: CAPTCHA (protondrive.md:35-37), 2FA prompting (:131-147) and "rclone about reports the whole account" (:548) are unverified live, while docs/TODO.md says the docs "avoid claiming" 2FA and CAPTCHA. Either soften or fix the TODO wording.
  16. Lost TODO from the live run: the ledger's 2026-11-12 incident note ("rclone config create printed the new remote's client_* tokens and salted key pass to stdout ... check whether client_* options are marked Sensitive") is not in docs/TODO.md. The options are Sensitive (protondrive.go:113-142), but config create/config show print unredacted (fs/config/ui.go ShowRemote) — generic rclone behaviour, yet worth a docs line ("the output of rclone config create contains the session; don't paste it") and a TODO entry, since the salted key password survives a logout.
  17. docs/PERMISSIONS.md / .codex/ out of step: golangci-lint run was added to the Allow tier and to .claude/ and .gemini/, but not to .codex/rules/project.rules, against AGENTS.md's "change them together".
  18. 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 a git push --mirror or a backup bundle of this checkout isn't made before then.
  19. Handoff ruling 13 still reads as current ("stays in go.mod until Task 19"); mark it done (Task 19 review minor, still present).