Skip to content

'club remove' silently leaves a stale current_club, and jobs still run against the removed club #174

Description

@Sootopolis

Removing the club you are currently using produces no error and no warning at any layer — and the next bare command runs a real job against the club you just disowned.

Chain:

  • Dispatcher.scala:212-215 (ClubsRemove) deletes the marker and prints. It never reads currentClub, despite runCommand receiving it (Dispatcher.scala:93).
  • Server-side only managed_club + that club's job_schedule rows are removed (ManagedClubRoutes.scala:50-62). The club row survives — the FK is ON DELETE RESTRICT with club as parent.
  • ClubResolver.single does zero validation and returns the stale current_club verbatim (ClubResolver.scala:22-26).
  • Job submission gates on Club.selectBySlug, not managed status (JobRoutes.scala:428-435). The club row is still there, so the lookup succeeds and the job runs.

User-visible result: after ccas club remove team-alpha, a bare ccas membership / history / recruit / stats submits and runs a full job against team-alpha — Chess.com API traffic and DB mutation. For ccas recruit --stdout this is destructive: it auto-confirms invites (Dispatcher.scala:137), burning real candidates on a disowned club. Nothing in the output hints at it.

Sibling inconsistency: --all correctly excludes the removed club (it reads the live managed set, ClubResolver.scala:37), while the bare/current_club path includes it. Same club, two targeting modes, opposite results.

Also: current_club can't be cleared at all. ConfigWriter exposes only setCurrentClub (no clear/unset), UseClub rejects a blank slug with exit 2 (UseClub.scala:17-19, pinned by TestUseClub.scala:11-13), and ccas config unset targets the other file.

Proposal — auto-clear and say so. Warn-only leaves the stale pointer live; blocking the removal is wrong (the user's intent is clear).

  1. Add ConfigWriter.clearCurrentClub — reuse the existing CurrentClubAssignment regex and the atomic temp+rename.
  2. In the ClubsRemove branch, compare the removed slug to currentClub case-insensitively after .trim; on a match, clear and print that the current club was cleared.
  3. Surface the primitive as ccas use-club --clear for manual fixes.

The next bare command then fails loudly with the existing, already-good ClubResolver.NoClubError.

No test covers any of this — TestClubResolver.scala:23-43 covers precedence among present values only.

Durable fix is the server-side managed gate, filed separately.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:cliCLI binary (subcommands, completions, config, packaging)area:serverBackend HTTP server, jobs, schedulerbugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions