Sibling of the read-side fix on feat/club-cli-ux (#173 follow-up). CompletionCache's four write paths — writeClubsIn, seedClubsIn, appendJobIn, invalidateIn — all end in .ignore. If the cache directory is unwritable, the cache never populates, silently, forever: shell completion simply does nothing and nothing ever says why.
Reproduced (no server needed):
XDG_CACHE_HOME=$BASE/cache # writable root
mkdir -p $XDG_CACHE_HOME/ccas && chmod 500 $XDG_CACHE_HOME/ccas
# config.conf: default_clubs = ["team-alpha", "team-beta"]
$ ccas use-club
team-alpha # exit 0, no mention of the cache
$ ls $XDG_CACHE_HOME/ccas/ # empty — clubs.txt was never created
seedClubs attempted the write, failed, and said nothing.
The read-side Option fix does not cover this. That distinguishes "unreadable" from "empty", but here the file is absent, so readClubsIn correctly returns Some(Nil) and correctly stays quiet. The gap is purely on the write path.
Realistic trigger: a root-owned ~/.cache/ccas left behind by a single sudo ccas run (same root cause as the unreadable-file case), a read-only home, or a misconfigured XDG_CACHE_HOME.
Recommended shape
Do not warn at every write site. seedClubs runs on every invocation (Main.scala:46, before parsing), so a broken dir would nag on ccas --help, ccas config get, and every other command.
Surface a failure only where the cache write is semantically part of what the user just asked for:
use-club <slug> — the user is setting up club context; the refresh is part of making it work.
club add / club remove — these mutate the managed set, which is the cache's source.
Stay silent on incidental writes: the post-command .tap refresh in Dispatcher, appendJob, seedClubs.
Mechanically that means only writeClubs (and arguably invalidate) stop being .ignore and return a success signal; those two or three call sites decide whether to emit one stderr line. Never change an exit code — a broken cache is not a failed command.
Cheap complementary option worth considering in the same issue: have ccas completion <shell> check cache-dir writability as it emits the script and warn on stderr if not writable. That is exactly the moment the user is installing completion, costs one stat, and adds no noise anywhere else — though it only catches setup-time breakage, not a dir that breaks later.
Also worth deciding
clubsStaleIn's .orElseSucceed(true) has the same error-vs-normal conflation but is fine as-is — an error maps to "stale", which just triggers a refresh. That is the safe default and self-corrects, so no information is lost. Recommend leaving it and noting why.
Sibling of the read-side fix on
feat/club-cli-ux(#173 follow-up).CompletionCache's four write paths —writeClubsIn,seedClubsIn,appendJobIn,invalidateIn— all end in.ignore. If the cache directory is unwritable, the cache never populates, silently, forever: shell completion simply does nothing and nothing ever says why.Reproduced (no server needed):
seedClubsattempted the write, failed, and said nothing.The read-side
Optionfix does not cover this. That distinguishes "unreadable" from "empty", but here the file is absent, soreadClubsIncorrectly returnsSome(Nil)and correctly stays quiet. The gap is purely on the write path.Realistic trigger: a root-owned
~/.cache/ccasleft behind by a singlesudo ccasrun (same root cause as the unreadable-file case), a read-only home, or a misconfiguredXDG_CACHE_HOME.Recommended shape
Do not warn at every write site.
seedClubsruns on every invocation (Main.scala:46, before parsing), so a broken dir would nag onccas --help,ccas config get, and every other command.Surface a failure only where the cache write is semantically part of what the user just asked for:
use-club <slug>— the user is setting up club context; the refresh is part of making it work.club add/club remove— these mutate the managed set, which is the cache's source.Stay silent on incidental writes: the post-command
.taprefresh inDispatcher,appendJob,seedClubs.Mechanically that means only
writeClubs(and arguablyinvalidate) stop being.ignoreand return a success signal; those two or three call sites decide whether to emit one stderr line. Never change an exit code — a broken cache is not a failed command.Cheap complementary option worth considering in the same issue: have
ccas completion <shell>check cache-dir writability as it emits the script and warn on stderr if not writable. That is exactly the moment the user is installing completion, costs one stat, and adds no noise anywhere else — though it only catches setup-time breakage, not a dir that breaks later.Also worth deciding
clubsStaleIn's.orElseSucceed(true)has the same error-vs-normal conflation but is fine as-is — an error maps to "stale", which just triggers a refresh. That is the safe default and self-corrects, so no information is lost. Recommend leaving it and noting why.