Summary

Squad and Crew were two model stacks over the same crews table, with two sets of names for the same columns and two services writing to it. Squad has been deleted; Crew absorbed what it carried.

DB columnName in SquadName in Crew
titlenametitle
valid_untilexpiresAtvalidUntil
member countcurrentSizememberCount

Every new column had to be added twice. visibility was added twice on the same afternoon it was created.

Key Decisions

It was not just duplication — the halves had drifted

SquadService.createSquad was a worse, divergent version of createCrew:

  • it never set a title, so those crews were named after their own vibe tag — which is why the crew list had rows called after their genre;
  • it wrote area_name = vibe_tag as the anchor, producing crews anchored to Techno;
  • it hardcoded a 24h expiry instead of deriving it from the event end;
  • it created its own chat with metadata: {'squad_id': …} — a different key from crew_id, so sweep_crew_chats() would never have found those chats: they would never close and never be purged;
  • and since trg_crews_create_chat now creates the chat with the crew, that path would have produced two chats per crew, overwriting chat_id with the second and orphaning the first.

Warning

The last point was a bug introduced the same morning by the trigger. It would have stayed invisible until someone created a crew from the feed.

One vocabulary, no aliases

Crew absorbed the derived state Squad carried: displayName (falls back to the vibe for the nameless crews the old path created), isFull, spotsLeft, isExpired, timeLeft, timeLeftLabel.

No name/title aliases were added. Two words for one field is the disease, not the cure — it would have been enough to restart the drift.

The rename reached the l10n layer too

15 keys renamed off “squad”, and the English, Italian and Spanish copy with them. The UI still said “JOIN SQUAD” and “Leave the Squad?” while the code said crew.

Note

A rename that stops at the code layer does not resolve the confusion. It moves it onto the user.

Files Changed

Deleted:

FileWhy
lib/shared/models/squad_model.dartsecond model over the same table
lib/features/social/services/squad_service.dartsecond, divergent write path
lib/features/social/repositories/squad_repository.dart
lib/features/social/notifiers/squad_notifier.dartduplicated CrewNotifier
lib/features/social/screens/create_squad_sheet.dartcreated nameless crews anchored to the vibe
lib/features/social/providers/crew_provider.darta third stack (ChangeNotifier beside the StateNotifier), zero references anywhere
app_config_service.squadExpiryHoursdefined once, called never

Renamed: squad_bento_section.dartcrew_bento_section.dart, squad_detail_sheet.dartcrew_detail_sheet.dart.

−1171 lines, +319.

Architecture Notes

Fixed in passing: event_discovery_screen renders a crew section but never asked for the crews. It drew whatever the feed had loaded on its own, so opening discovery first showed an empty row. It now loads its own.

Open Items

  • chats still holds rows with metadata.squad_id written by the old path. They are attached to no crew and the sweep does not see them. Worth checking and removing if empty.
  • crews.title is nullable and some historical rows are empty. displayName covers that on screen, but the column should become NOT NULL now that the only creation path always asks for it.

feature-crew-chat-lifecycle — the trigger that made the double path actively harmful fix-crew-event-link — the preceding chain of bugs localization — renamed keys