A club-slug rename UPDATEs the club row in place (Club.scala:90-98) — no history row, no alias table. The old slug ceases to exist the instant recovery lands.
Stale completion suggestion. Invalidation is mtime-vs-6h only (CompletionCache.scala:20,37-44), refreshed opportunistically by a .tap on any server-touching command (Dispatcher.scala:51,78-87). Nothing in the rename path touches clubsFile. Window = 6h plus however long until the next successful command — lower bound 0s, upper bound unbounded.
The failure is unrecoverable in-flight. JobRoutes gates on an exact-equality Club.selectBySlug (JobRoutes.scala:143-144, :428-429) before submission, so ClubSlugRenameResolver — which lives downstream inside the apps (MembershipApp.scala:129) — is structurally unreachable for exactly the case it was built for. The CLI renders <old-slug>: Club not found, exit 1 (JobFollower.scala:247-249), indistinguishable from a typo or a never-seen club.
The error doesn't bust the cache. The .tap runs but re-gates on the 6h mtime check (Dispatcher.scala:79), so an immediate retry gets the same stale suggestion. CompletionCache exposes no invalidate method.
current_club is worse — never revalidated or repointed, so a renamed current club fails permanently until the user manually re-runs use-club, with no message saying so.
Proposal:
- Add
CompletionCache.invalidate (delete clubsFile) and call it when a ClubJobResult/JobResult carries Club not found, letting the existing .tap repopulate on the same invocation.
- When the failing slug equals
current_club, append a hint: the current club no longer resolves, set a new one with ccas use-club <slug>.
- Larger / not costed: a
club_slug_history (club_id, slug, seen_from, seen_until) table written from Club.upsert / resolveStaleSlug would let a stale-slug request resolve in one indexed lookup, turn the error into "club 'old' was renamed to 'new'", and let use-club auto-repoint. Migration cost and write-path contention unassessed.
A club-slug rename UPDATEs the
clubrow in place (Club.scala:90-98) — no history row, no alias table. The old slug ceases to exist the instant recovery lands.Stale completion suggestion. Invalidation is mtime-vs-6h only (
CompletionCache.scala:20,37-44), refreshed opportunistically by a.tapon any server-touching command (Dispatcher.scala:51,78-87). Nothing in the rename path touchesclubsFile. Window = 6h plus however long until the next successful command — lower bound 0s, upper bound unbounded.The failure is unrecoverable in-flight.
JobRoutesgates on an exact-equalityClub.selectBySlug(JobRoutes.scala:143-144,:428-429) before submission, soClubSlugRenameResolver— which lives downstream inside the apps (MembershipApp.scala:129) — is structurally unreachable for exactly the case it was built for. The CLI renders<old-slug>: Club not found, exit 1 (JobFollower.scala:247-249), indistinguishable from a typo or a never-seen club.The error doesn't bust the cache. The
.tapruns but re-gates on the 6h mtime check (Dispatcher.scala:79), so an immediate retry gets the same stale suggestion.CompletionCacheexposes no invalidate method.current_clubis worse — never revalidated or repointed, so a renamed current club fails permanently until the user manually re-runsuse-club, with no message saying so.Proposal:
CompletionCache.invalidate(deleteclubsFile) and call it when aClubJobResult/JobResultcarriesClub not found, letting the existing.taprepopulate on the same invocation.current_club, append a hint: the current club no longer resolves, set a new one withccas use-club <slug>.club_slug_history (club_id, slug, seen_from, seen_until)table written fromClub.upsert/resolveStaleSlugwould let a stale-slug request resolve in one indexed lookup, turn the error into "club 'old' was renamed to 'new'", and letuse-clubauto-repoint. Migration cost and write-path contention unassessed.