 function buildRows(historyKey: TGKey): ResultRow[] {
   const { slot, date }     = TGKey.parse(historyKey);
   let players: UserKey[]   = Attendance.players(historyKey);
   const rows: ResultRow[]  = [];
   const weekKey  = WRank.weekKey(new Date(date));
 
  // Fallback — read from history file directly
  if (players.length === 0) {
    const history = Store.read<{ scores: TGScore[] }>(TGKey.toHistoryPath(historyKey));
    if (history?.scores) {
      players = [...new Set(history.scores.map((s: TGScore) => s.userKey))];
    }
  }

   for (const userKey of players) {
     const chars = CharacterRegistry.forUser(userKey);
     // Find which character this user played in this TG
     // by checking score history
     for (const char of chars) {
       const score = Score.get({ character: char, slot, historyKey });
       if (!score) continue;  // skip chars with no score for this TG
 
       const wrEntry = WRank.entry(char.name, char.nation, weekKey);

       rows.push({
         character:   char,
         score,
         position:    wrEntry ? {
           currentRank:  wrEntry.currentRank,
           previousRank: wrEntry.previousRank,
         } : undefined,
         leavesCount: Leaves.countForChar({ characterName: char.name }),
       });
       break; // found the char for this user, move to the next user
     }

     if (!rows.find(r => r.character.ownerKey === userKey)) {
      // Build from score directly
      const history = Store.read<{ scores: TGScore[] }>(TGKey.toHistoryPath(historyKey));
      const score   = history?.scores.find((s: TGScore) => s.userKey === userKey);
      if (score) {
        const foundChar = CharacterRegistry.find(score.characterName);
        const char: Character = foundChar ?? {
          name:     score.characterName,
          class:    CLASSES[score.class as ClassKey] ?? { key: score.class as ClassKey, name: score.class, shortName: score.class },
          level:    0,
          nation:   score.nation,
          ownerKey: score.userKey,
        };
        rows.push({ character: char, score, leavesCount: 0 });
      }
    }
   }
 
   return rows;
 }