Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8.6 KiB
Task 18 review (d211b45e6..28f1aaf8a)
Spec Compliance
- ✅ Spec compliant, with the handoff corrections applied. Every listed file has a hunk: init.d script, PORTS.md (28642, next free), config.yaml entry (
fastlist: false),.gitea/workflows/ci.yml, TODO.md, PERMISSIONS.md. The plan's step 1 allowedignoretestsonly for bisync/gitannex; the extraignoreof five tests is a deviation the implementer flagged and the brief's "fix backend failures until clean" could not satisfy. I judge it acceptable (see cache analysis) but it is a decision for the owner. - ⚠️ Cannot verify: ci.yml on a real Gitea runner (label
ubuntu-latest,uses:resolution ofactions/*andgolangci/golangci-lint-action, network for govulncheck). TODO.md records this, as required..claude/settings.jsonnot edited: correct, not counted.
Strengths
- Init script lifecycle is sound. I ran it in a scratch
STATE_DIR(RCLONE_CONFIG=/notfound, nothing but loopback):- start in under 1s, six key=value lines plus
_connect; a second start reuses the server (refcount 2); first stop keeps it, second stop kills it; no protonfake and nothing on 28642 afterwards. - build failure (fake
gothat exits 1):failed to start, rc=1, no process left. - port already taken: protonfake exits, script reports its log and
failed to start, rc=1, no orphan. shellcheck: only SC2034 (unused loop vari), warning level.
- start in under 1s, six key=value lines plus
- Built binary instead of
go run, so the pid is the server's. Output goes to a file and stderr to a log, stdin is /dev/null, soCombinedOutputintestserver.rundoes not hang on an inherited pipe. - Repo root derived from the script location (
dirname "$0"/../../..); works from any package directory. - Cannot reach live remotes: the fake is a separate remote name
TestProtonDriveFake:configured only throughRCLONE_CONFIG_TESTPROTONDRIVEFAKE_*env (distinct prefix from..._TESTPROTONDRIVE_...), endpoint is 127.0.0.1, CI setsRCLONE_CONFIG=/notfound. The live entry is untouched. - DECISIONS.md entry for the shared session is accurate and records the revisit condition. TODO entries are specific and actionable. Stale "no Go on host" text removed from PERMISSIONS.md.
Cache diagnosis (the five ignored tests)
Diagnosis is right. nodeCache (backend/protondrive/cache.go:12-19) is per client/Fs (nodes.go:189). listChildren (nodes.go:753-757) and findChild (nodes.go:894-905) trust a cached complete listing, and a miss in it returns errNotFound with no server check. cache.add (cache.go:140-157) appends only to this cache's listing. So Fs B holding a complete listing of folder X never learns of a file Fs A creates in X. There is no TTL or any expiry anywhere in cache.go; only invalidate*/clear and the Move/DirMove cross-Fs invalidation.
Real user impact
--compare-dest/--copy-dest(+--immutable) /checksum --download: only wrong when the reference Fs's folder is written through another Fs of the same account in the same process, after the reference Fs listed it. Normal use (reference dir is pre-existing and untouched) is fine. Overlappingrclone sync remote:a remote:a/substyle commands are the realistic hit. Low frequency.- Mount + a second
rclonecommand: this is a separate, larger staleness, not the one the tests hit. Different processes share nothing, and the mount's cache has no TTL and no events (M5 absent), so the mount never sees external writes until restart; VFS--dir-cache-timerefreshes callList, which is served from the backend cache. The option help (protondrive.go:189-197) anddocs/content/protondrive.mdwarn about it, andenable_cachingdefaults to true. The implementer's TODO entry does not mention it; the user docs (Task 20) should keep the mount warning. - Wrong-answer severity: a stale miss makes
--compare-destre-copy a file (safe) but--copy-dest --immutableand Put-after-miss paths can attempt a create that the server rejects with 2500 (exists) rather than silently overwrite; I did not run this.
Fix options (recommend, not implement)
- Cheap and spec-compatible, helps mounts but not the five tests: a listing TTL (e.g. 1 to 5 minutes, only for listings, checked in
cache.children). Budget tests run in seconds, soTestBudgetUpload/TestBudgetMkdir/TestBudgetList(warm listing = 0 requests) are unaffected. Needs a clock injected intonodeCachefor tests. It does not cover the five tests, which finish in milliseconds. - Re-list on miss, whatever flavour (always, age-limited, or only in read paths): breaks the budgets, because
operations.CopycallsNewObject(a miss for new files) before every Put; any per-miss server check is a request per new file. A name-hash existence probe (check_available_hashes) is one request per miss: same budget break. An age gate ("only re-list if the listing is older than N ms") would be timing dependent and still fail tests that run within N. - A cache shared by the Fs of one account fixes the tests properly but needs registry state keyed by account; the spec's no-package-state rule forbids it. The only spec-compatible shape is something owned by a shared object the Fs already receive, and none exists. Owner decision, as the implementer says.
- Test-side alternative to
ignore: run those five against a second fake remote withenable_caching=false(they pass uncached per the report) so they still cover the backend's uncached path. Costs a second init.d script and port, so probably not worth it before M5.
Recommendation: keep ignore, keep the TODO, add the mount/no-TTL point to the TODO entry, and consider option 1 as a small separate task. Events (M5) are the right real fix.
Issues
Critical
None.
Important
None.
Minor
fstest/testserver/init.d/TestProtonDriveFake:55-61stop(lines 63-76) sends only SIGTERM and after 10 s removes the pid file whether or not the process died, so a hung protonfake would be orphaned and untracked (port stays bound; the next start then fails with "address already in use"). Addkill -9after the wait loop. Low likelihood (the fake exits on SIGTERM).TestProtonDriveFake:57unused loop variablei(shellcheck SC2034); usefor _ in $(seq ...).TestProtonDriveFake:24build output goes to stderr, whichtestserver.runmerges with stdout viaCombinedOutputand then parses with^key=value. A go build diagnostic that happened to look likea=bwould be exported as an env var. Not realistic; redirecting build output to${RUN_STATE}/build.logand printing it only on failure would remove the risk.TestProtonDriveFake:24rebuilds on every first start with a coldgo build, andtestserver.starthas no timeout. On a fresh slow runner with a cold build cache this could take minutes but works. Acceptable..gitea/workflows/ci.yml:5-7push:andpull_request:with no filter and noconcurrencygroup: a push to a PR branch runs the job twice, and superseded pushes keep running. On a slow runner this contradicts "light". Addconcurrency: {group: ci-${{ github.ref }}, cancel-in-progress: true}, or limitpushtomaster.ci.yml:44-48fuzz loop:go test -listcompiles the test binary, and each of the 5-fuzzruns links again (about 5 compiles plus 100 s of fuzzing). Fine but not cheap; running all targets as onego test -fuzzis impossible, so this is the minimum.go-version-file: go.modgives Go 1.26.0, while golangci-lint v2.15.0 is a prebuilt binary; if its build Go is older than the module'sgo 1.26.0the lint step fails ("version lower than targeted"). Host lint passed with Go 1.27.4 only. Worth watching on first push.ci.yml:26,51go run ...govulncheck@latestis unpinned, so the job can break without a code change (brief mandated it).go build ./...builds all of rclone, the heaviest step on a slow runner (brief mandated it).docs/TODO.md"Now" entry: the lineRemaining: Task 19 (...is a very long line, unlike the surrounding wrapped text (cosmetic).fstest/test_all/config.yaml: a bare-backends protondrivewithout-remotesruns both the fake and the liveTestProtonDrive:entries. Pre-existing behaviour, but now that two entries exist, PERMISSIONS.md should say always pass-remotes TestProtonDriveFake:(the verified-commands row does).
Assessment
Task quality: Approved
Reasoning: The script's start/stop/refcount/failure paths work as run, nothing can reach the live remotes, and CI plus docs match the brief; remaining items are minor hardening. The five ignored tests are a genuine consequence of per-Fs caches with no expiry, and the implementer's refusal to add package state or a per-miss re-list is correct given the budget tests.