Balayage mécanique du site ASA Compass

2 231 pages publiées lues une à une par script, 27 pages ouvertes dans un vrai Chrome, 13 807 médias uniques contrôlés par requête. Défauts classés par cause dans le générateur, jamais par page.

Version parcourue
Déploiement Cloudflare 168bcff0 du projet asa-compass-site, mis en ligne le 31 août 2026 vers 23 h (heure de Montréal). Le contenu servi est identique octet pour octet au dossier site-public/ engendré le 31 août à 22 h 50 (pages) et 22 h 53 (fichiers recopiés) : vérifié par empreinte MD5 sur huit fichiers dont index.html (31f541d1…), collections.html (d7ce2fc9…), pieces.json, portefeuille.js et une page de collection.
Générateur lu
sonde/site.mjs dans son état du 1er septembre à 06 h 38. Il a bougé depuis la génération publiée (exemple : le registre des ventes d'une page de collection montre 16 lignes en ligne, la source dit aujourd'hui slice(0, 12)). Les numéros de ligne cités renvoient à cet état de la source ; chaque défaut a été constaté sur le site en ligne, pas déduit de la source.
Balayage fait le
1er septembre 2026, de 07 h à 08 h 30 (heure de Montréal).
Ce que ce rapport ne juge pas
La mise en page, la hiérarchie visuelle, les couleurs, le choix des mots. Seulement la mécanique : ce qui mène nulle part, ce qui manque, ce qui s'affiche vide ou faux, ce qui ne répond pas ou répond deux fois.

Résumé, dans l'ordre où réparer

#CauseToucheCe que le visiteur voit
1Année de première frappe calculée avec un seuil de round différent de celui de l'index996 pages« first minted 2022 » sur une page dont toutes les pièces sont datées d'octobre à décembre 2021, et que l'index range sous 2021
2Historique par compte engendré avant la reconstruction du catalogue, avec des adresses de pages figées1 184 portefeuilles, 2 022 lignesDans le Portfolio, onglet History, des liens vers 37 collections dont la page n'existe plus ; Cloudflare y sert l'accueil
3Le tri « Most traded » de l'index n'a aucune donnée : toutes les tuiles portent un volume de 02 222 tuilesChoisir « Most traded » ne change rien à l'ordre
4L'index des pièces cherche les collections regroupées sous un titre qui n'existe pas dans la base31 collections, 2 810 piècesLe Portfolio ne reconnaît jamais ces pièces, même quand le visiteur les détient
5Un compteur de la page « Read me » compare un tableau à zéro1 page« Spread across 0 collections » sous les 51 579 ventes
618 575 fichiers déployés sur une limite de 20 000, dont 15 314 images de repli jamais demandéestout le déploiementRien encore ; le prochain millier d'images fera échouer la mise en ligne entière
7Aucune page 404 : tout chemin inconnu rend l'accueil avec un code 200toute adresse fausseUn lien mort ressemble à un lien qui marche ; c'est ce qui a caché les défauts 2 et « /c/null.html »
8Image Open Graph en chemin relatif2 231 pagesAucune vignette quand un lien du site est partagé sur un réseau social ou une messagerie
9Les quatre onglets de l'index reçoivent deux écouteurs de clic1 page, 4 boutonsRien aujourd'hui ; c'est le motif exact du bouton de filtres, prêt à se rejouer
10Page d'une pièce : l'adresse de l'actif est prise pour une image, et les lectures de chaîne n'ont pas de délaicas limitesUn cadre vide et « 0 holder » sur un jeton fongible, « Reading the chain… » pendant 6 à 8 s

Défaut 1 996 pages L'année de première frappe contredit l'index et les pièces

Ce qui se voit. La page /c/algopumpkin annonce « first minted 2022 » dans son bandeau et « minted in 2022 » dans sa description, alors que ses huit pièces portent « Oct 2021 » et que l'index la range sous l'onglet 2021. Sur 2 222 pages de collection, 1 161 disent 2022 ; l'index n'en compte que 165 sous 2022. 912 pages se contredisent visiblement (l'année du bandeau n'apparaît sur aucune de leurs pièces), et 996 sont touchées au total.

La cause. Deux calculs de la même année. pageCollection() (site.mjs ligne 4675) écrit c.premier_round < 17000000 ? 2021 : 2022, un seuil posé à la main. Tout le reste du site (index, onglets, mois, dates des pièces) passe par anneeDeCollection() et dateDuRound(), dont la borne de 2022 est le round 18 363 444. Les collections nées entre les rounds 17 000 000 et 18 363 443, soit de la fin octobre à la fin décembre 2021, tombent entre les deux : la première est algopumpkin (round 17 000 521), la dernière degen-forever-series (18 363 421), et monsti (16 999 873) est correcte à 648 rounds près.

Reproduire. grep -l "first minted 2022" site-public/c/*.html | wc -l donne 1 161 ; la même commande sur l'index (data-annee="2022") donne 165.

Correction. Une ligne : remplacer le seuil par anneeDeCollection(c), qui existe déjà et sert partout ailleurs. Vérifier ensuite que les 2 222 pages tombent dans la même année que leur tuile d'index (data-annee).

Défaut 2 1 184 portefeuilles L'historique par compte pointe vers des pages qui n'existent plus

Ce qui se voit. Un visiteur qui connecte un portefeuille ayant échangé des AlgoDroid, des Yieldling ou des YldYcorn voit dans l'onglet History du Portfolio des lignes qui mènent à /c/algodroid.html, /c/yieldling-ultra-rare.html, /c/yldycorn-generation-a.html. Ces pages n'existent pas dans la version publiée : Cloudflare y répond l'accueil, avec un code 200. 37 adresses de collection sont dans ce cas, sur 2 022 lignes d'historique réparties dans 1 184 portefeuilles (sur 103 158 lignes au total). Aucun des 37 titres n'existe sous une autre adresse dans le catalogue actuel : ces collections ont été absorbées ou renommées par la reconstruction.

La cause. C'est le motif de /c/null.html, une troisième fois. Les tranches sonde/resultats/comptes/*.json datent du 31 août à 09 h 18 ; le catalogue a été reconstruit à 17 h 20 et le site engendré à 22 h 50. index-comptes.mjs recopie l'adresse de page depuis ventes.json (l: v.limace ?? null, ligne 44), où elle avait été figée par collecte-ventes-completes.mjs (lignes 88 et 148) au moment de la collecte. site.mjs, lui, recalcule l'adresse de chaque vente à la génération, par titre (ligne 5535), ce qui est la bonne méthode ; mais il recopie comptes/ tel quel (lignes 5679 à 5686). Et reconstruire.sh ne relance jamais index-comptes.mjs : ce n'est pas l'une de ses sept étapes.

Reproduire. https://asa-compass-site.pages.dev/c/algodroid rend le titre de l'accueil. Dans site-public/comptes/22.json, la première entrée porte "l":"algodroid".

Correction. Que index-comptes.mjs recalcule l'adresse par titre depuis catalogue-public.json, comme le fait site.mjs, au lieu de lire celle de ventes.json ; et qu'il devienne une étape de reconstruire.sh, après site.mjs, puisqu'il dépend des adresses que celui-ci attribue. Même chose pour le champ limace de ventes.json, qui ne devrait pas exister : une adresse de page est une donnée dérivée.

Défaut 3 2 222 tuiles Le tri « Most traded » ne trie rien

Ce qui se voit. Sur l'index, choisir « Most traded » dans la roulette laisse la grille dans l'ordre chronologique : première tuile AlgoKitten, dernière ALGOegg Fellowship, exactement comme « Oldest first ». Constaté dans Chrome, à 1 280 comme à 375 points.

La cause. Les 2 222 tuiles portent data-volume="0". pageCollections() remplit ce champ avec c.volume ?? 0 (ligne 5291), et aucune collection du catalogue ne possède de champ volume : ses clés sont cle, titre, pieces, collections, createurs, listeCreateurs, note, premier_round, ids, rarete, derniereActivite, rang, limace, ventes, vignettes. Le comparateur du navigateur (ligne 2511) fait 0 - 0 puis retombe sur le round.

Reproduire. grep -o 'data-volume="[0-9]*"' site-public/collections.html | sort | uniq -c : une seule valeur, 0, 2 222 fois.

Correction. Le volume se recalcule à la génération depuis c.ventes, qui est rattaché à chaque collection (ligne 5629) avant l'appel de pageCollections() : la somme des prix suffit, ou le nombre de ventes si c'est ce que Karl entend par « traded ». Ne pas l'inscrire dans le catalogue.

Défaut 4 31 collections Le Portfolio ne reconnaît jamais les collections regroupées

Ce qui se voit. Un visiteur qui détient un Moon Dude (547 pièces publiées), un World Chess (578), un Astro Buddies (203) ou l'une des 28 autres voit « none of them belong to the 2 222 collections published here », alors que la page de la collection existe et montre ses pièces. 31 collections, 2 810 pièces.

La cause. pieces-index.mjs retrouve les pièces d'une collection publiée en cherchant son titre exact dans la table collections de catalogue.sqlite (lignes 31 à 41). Les collections issues du regroupement par famille portent un titre de famille, « Moon Dude », « 16bit Tarot », qui n'existe pas dans la base : elle ne connaît que « Moon Dude Basic », « Moon Dude Fine », « 16bit Tarot - The Tower ». La requête ne rend rien, et pieces.json ne contient aucun de leurs identifiants.

Reproduire. La première vignette de /c/moon-dude est l'actif 307771525 ; pieces.json n'a pas de clé 307771525. Témoin : l'actif 197227528 d'AlgoKitten y est, rang 0.

Correction. Faire chercher les pièces par la liste des collections membres (le champ collections ou ids du catalogue) plutôt que par le titre affiché. À noter aussi : pieces.json date du 31 août à 17 h 43 et n'a pas été régénéré avec le site de 22 h 50 ; les adresses y correspondent encore toutes à des pages, mais rien ne le garantit à la prochaine reconstruction si l'étape 7 de reconstruire.sh est sautée.

Défaut 5 1 page « Spread across 0 collections » sur la page Read me

Ce qui se voit. Sur Read me, sous « 51 579 trades recovered » : « Spread across 0 collections. Everything else never changed hands on-chain. » Le site compte 646 collections avec des ventes.

La cause. pageLisezMoi() ligne 5038 : collections.filter((c) => (c.ventes || 0) > 0). Au moment de l'appel, c.ventes est un tableau (rattaché ligne 5629). Un tableau vide vaut false devant || et donne 0 ; un tableau plein comparé à 0 donne NaN > 0, faux. Le compteur est donc toujours 0.

À côté, deux nombres qui divergent pour la même chose. L'accueil annonce 50 568 ventes (ventesLiees, celles rattachées à une page) ; Read me en annonce 51 579 (ventes, toutes). Et l'accueil affiche « 1,891,551 items found on-chain » et 37 624 créateurs depuis une constante figée dans la source (ligne 5635, const totaux = { pieces: 1891551, createurs: 37624 }) qu'aucune reconstruction ne met à jour.

Correction. (c.ventes?.length ?? 0) > 0. Décider quelle liste de ventes fait foi et la passer aux deux pages. Recalculer les totaux de chaîne à la génération ou les dater.

Défaut 6 tout le déploiement18 575 fichiers sur une limite de 20 000

Ce qui se voit. Rien encore. Mais Cloudflare Pages refuse un déploiement de plus de 20 000 fichiers (limite documentée, non mesurée ici), et site-public/ en compte 18 575 : 2 231 pages, 1 024 tranches d'historique, 15 314 fichiers dans img/ (564 Mo). Le catalogue a doublé le 31 août ; le prochain palier d'extraction d'images fera échouer la mise en ligne entière, avec un message que le projet n'a encore jamais vu.

Ce que img/ fait vraiment. Aucune des 2 231 pages n'y fait référence directement : toutes les vignettes viennent du cache asa-compass-medias.karlgrandmaison.workers.dev/media/vignette/. Le dossier n'est demandé que par un gestionnaire d'erreur du script commun, qui remplace l'adresse du cache par /img/… quand une image échoue (ligne 3421, el.dataset.repli = '1'). Or le contrôle de ce balayage a interrogé les 13 807 médias uniques du site : 13 807 réponses 200 avec le bon type (13 482 image/jpeg, 325 video/mp4) et une taille non nulle (la plus petite, 782 octets, est une vraie image). Le repli n'a jamais été sollicité. Le pied de page dit d'ailleurs encore « images served from IPFS », ce qui n'est plus vrai.

Correction. Cesser de recopier img/ dans le site (site.mjs lignes 5651 à 5657) et faire porter le repli par le cache lui-même, qui sait déjà rattraper un média manquant auprès des passerelles. À défaut, ne recopier que les vignettes réellement référencées. Dans les deux cas, faire compter les fichiers par la génération et refuser au-delà d'un seuil.

Défaut 7 toute adresse fausse Pas de page 404

Ce qui se voit. /page-inexistante-xyz, /c/null, /c/algodroid : l'accueil, code 200, sans un mot. Le projet le sait et s'y est fait prendre deux fois ; le défaut 2 s'y cache encore aujourd'hui.

La cause. Cloudflare Pages sert index.html pour tout chemin inconnu tant qu'il n'existe pas de 404.html à la racine du dossier déployé. Le générateur n'en écrit pas.

Correction. Écrire un 404.html dans site.mjs, à côté de help.html : Cloudflare le sert alors avec un vrai code 404, et tout contrôle de liens, y compris celui de Karl en cliquant, voit enfin la différence entre une page et une absence. C'est la correction qui protège toutes les autres.

Défaut 8 2 231 pages Image Open Graph relative

Ce qui se voit. Un lien du site collé dans Discord, X, iMessage ou Telegram n'affiche aucune vignette. TETE (ligne 2031) écrit <meta property="og:image" content="/marque/logo.png"> ; le protocole Open Graph exige une adresse absolue, et les lecteurs de prévisualisation ignorent un chemin relatif. Toutes les pages sont touchées.

Correction. Préfixer par l'origine publique du site. Elle n'est pas encore choisie (adresse pages.dev aujourd'hui, domaine plus tard) : la lire d'une variable d'environnement à la génération.

Défaut 9 4 boutons Deux écouteurs de clic sur chaque onglet de l'index

Ce qui se voit. Rien aujourd'hui. Un espion posé avant le chargement de /collections compte deux écouteurs click sur chacun des boutons All, Favourites, 2021 et 2022. Sur l'accueil et sur les pages de collection, aucun élément n'en porte deux.

La cause. Deux scripts branchent la même barre. Le bloc du pied de page (ligne 2286) branche toute barre .periodes et bascule le panneau désigné par data-cible, ce qui sert au palmarès de l'accueil ; le script de l'index (ligne 2492) branche les mêmes boutons par data-onglet. Comme les onglets de l'index n'ont pas de data-cible, le premier écouteur cherche getElementById(undefined) et ne fait rien. Le jour où un onglet de l'index recevra un data-cible, ou où le pied de page changera de critère, la bascule se fera deux fois et s'annulera : c'est exactement ce qui est arrivé au bouton de filtres.

Correction. Restreindre le bloc du pied de page aux barres qui ont un data-cible (.periodes:has([data-cible]) ou un attribut sur la barre), ou retirer les onglets de l'index de la classe .periodes.

Défaut 10 cas limites La page d'une pièce prend l'adresse de l'actif pour une image

Ce qui se voit. /a?id=31566704 (USDC) affiche « Reading the chain… » pendant six à huit secondes, puis un cadre média vide et « HELD BY 0 WALLET », « 0 holder ». Le cadre est vide parce que la page place l'adresse de l'actif (https://www.centre.io/usdc) dans un <img>. Le compte de détenteurs est faux parce que la lecture des soldes est plafonnée. Sur les NFT du catalogue, la page fonctionne : AlgoKitten_001 charge son image pleine par bit.ly (6 220 pixels de large) en six secondes, Gravelight retombe sur la vignette du cache. Un identifiant absent, inexistant ou non numérique donne chacun un message clair.

Pourquoi c'est mineur. On n'arrive sur cette page que depuis une collection, et le catalogue ne contient pas de jetons fongibles. Cela devient un défaut le jour où l'adresse d'une pièce est partagée à la main, ce qui est le but de la page.

Correction. Un délai sur les lectures de chaîne, avec message ; ne mettre dans le cadre média que ce qui répond avec un type image/ ou video/ ; dire « fungible asset » quand le tirage n'est pas 1.

Ce qui a été vérifié et qui tient

Chaque ligne est un contrôle réel, pas une lecture de source.

Ce qui n'a pas pu être vérifié, et pourquoi

Méthode et outillage

Tout vit dans balayage-site/, à côté du projet, sans rien modifier ailleurs.

Le chantier régénère le site dans les heures qui viennent. Pour savoir si un défaut a survécu, rejouer node balayer.mjs puis node images.mjs depuis balayage-site/ sur le nouveau site-public/, et comparer les compteurs anneeFrappeContreditParToutesPieces2021 (défaut 1, doit tomber à 0), la présence de data-volume="0" (défaut 3), et la commande de comparaison des adresses de comptes/ (défaut 2).

Rapport de la session de balayage, 1er septembre 2026. Aucun fichier de sonde/, site-public/ ni contrat/ n'a été modifié.