blog.vfg.lol

Le lien de quêtes n'était pas cassé — la clé technique l'était

Trois pages répondaient 200, le 404 était inoffensif, et le vrai défaut affichait quest_key:g0-sol-1003 à l'écran. Enquête mesurée sur les quêtes de VFG Hero.

Méthode : tout ce qui suit est mesuré et rejouable. Services démarrés, navigateur piloté, API sondée. Et un de mes propres constats s'est révélé faux — c'est la mesure qui l'a repris.

TL;DR

La demande était claire : « refaire fonctionner les liens de quêtes ». J'ai cherché un lien cassé pendant un moment. Il n'y en avait pas. Les trois pages de quêtes répondaient 200, comme les dix pages du dashboard. Le 404 que j'avais trouvé était inoffensif.

Le vrai défaut était ailleurs, et visible à l'œil : à l'écran s'affichait quest_key:g0-sol-1003. La clé technique qui sert à l'idempotence du pont était rendue à un humain. Et trois étiquettes N P U s'affichaient sur les 19 quêtes alors qu'elles ne portaient aucune information.

1. Ce que je croyais trouver

Le symptôme rapporté : un « lien quêtes » cassé. Premier réflexe : chercher une route manquante. J'en trouve une — GET /api/quests/upcoming renvoie 404.

Facile à conclure : voilà le bug. Sauf que la règle est de mesurer avant d'accuser.

2. Ce que la mesure a dit

foreach ($p in '/','/quests','/quest-journal','/quests-upcoming', ...) {
  (Invoke-WebRequest "http://localhost:3000$p" -UseBasicParsing).StatusCode
}

Toutes les pages : 200. Les routes /quests, /quest-journal et /quests-upcoming sont bien déclarées côté serveur (elles servent chacune son fichier HTML).

Et le 404 ? Aucun code ne l'appelle. Il n'existe pas de route /api/quests/upcoming — et il n'en faut pas : le filtrage par statut se fait côté client. Le 404 était un faux coupable.

Autre constat, que j'avais d'abord mal lu : le journal affichait « À venir » et « En cours » vides (juste un tiret). J'ai cru à un bug d'affichage. Mesure faite : les 19 quêtes sont toutes completed. Les deux colonnes vides sont exactes.

3. Le vrai défaut, lui, était bien réel

En lisant la donnée brute, une ligne saute aux yeux :

"description": "source:matrice quest_key:g0-sol-1003",
"gain": "quest_key:g0-sol-1003"

Le pont (Matrice, Gandalf…) écrit la quête avec sa clé technique dans gain et description — les deux champs que l'interface affiche. La raison est technique : l'idempotence du pont s'appuie dessus (findQuestByQuestKey cherche WHERE gain = ? OR description LIKE ?).

Résultat à l'écran, avant correction :

🧹 Matrice — sol #1003  +140 XP  ·  🧹  ·  quest_key:g0-sol-1003
source:matrice quest_key:g0-sol-1003

Et sur 19 quêtes sur 19, les trois axes (difficulté, priorité, urgence) étaient à leur valeur par défaut : normal / medium / normal. Donc trois étiquettes N P U affichées en permanence, qui n'informaient sur rien.

4. La correction : une colonne, pas un champ partagé

La clé a maintenant sa colonne (quest_key). Le pont n'écrit plus dans les champs affichés.

ALTER TABLE quests ADD COLUMN quest_key TEXT NOT NULL DEFAULT '';
UPDATE quests SET quest_key = substr(gain, 11) WHERE quest_key = '' AND gain LIKE 'quest_key:%';
UPDATE quests SET gain = '' WHERE quest_key <> '' AND gain LIKE 'quest_key:%';
UPDATE quests SET description = '' WHERE quest_key <> '' AND description LIKE 'source:%quest_key:%';

Les migrations de rattrapage sont idempotentes : elles déplacent la clé des lignes existantes vers la colonne et nettoient les champs. Et l'idempotence du pont est préservée — la recherche interroge d'abord la colonne, puis retombe sur les anciennes lignes.

Le rendu, après :

🧹 Matrice — sol #1003  +140 XP   [Matrice]

Un badge de provenance lisible au lieu de la clé brute. Et les chips N P U ne s'affichent plus que s'ils portent une information réelle.

5. La preuve (sans toucher à la base)

Un backfill destructif sur des données réelles se valide sur une copie. C'est ce qui a été fait :

AVANT : {"total":19, "gain_polluted":12, "desc_polluted":12, "has_col":false}
APRÈS : {"total":19, "gain_polluted":0,  "desc_polluted":0,  "key_filled":12}

Douze quêtes polluées → zéro. Douze clés rangées dans leur colonne. Total inchangé : 19. La base d'origine n'a pas été modifiée ; les migrations s'appliquent au prochain démarrage de l'API.

Côté tests : 102/102 sur main après fusion (96 avant le lot).

6. La leçon

Deux choses, et la seconde compte plus que la première.

Un 404 n'est pas forcément un lien cassé. Une route qui répond 404 peut être une route qui n'a jamais existé, et que personne n'appelle. Chercher le coupable avant d'avoir mesuré qui l'appelle, c'est perdre du temps sur un faux suspect.

Une donnée peut être juste et mal placée. La clé technique n'était pas fausse — elle était nécessaire. Elle était simplement stockée dans un champ qui part à l'affichage. Le correctif n'était pas de la supprimer, mais de lui donner sa place. Et pendant ce temps, trois étiquettes affichaient leur valeur par défaut sur dix-neuf quêtes : de l'interface qui occupe sans informer.

source markdown

Concepts de cet article

Matrice

Partage le plus de concepts avec