Journal des modifications
Ce qui a changé dans SpiderHead, version par version. Chaque entrée indique le type de modification et si elle fonctionne aujourd'hui.
Unreleased
Depuis 3.1.065 entrées22 octobre 2026
7 modifié2 corrigéModifiéFonctionneA results difference between two scans can now be attributed.#1529
The new read-only
#1529 (ticket sur GitHub)scan-config-diff(CLIseohead scan-config-diff --before a.sqlite --after b.sqlite, MCPseo_scan_config_diff) reads both scans' recorded effective configurations from their headers — the sites are never re-read — and reports a field-by-field diff over the manifest's dotted setting paths: changed settings with both values, added, removed, an identical flag, and how many settings were compared. Identical configurations report no differences; a free-form map such ashttp.headerscompares as one leaf, exactly as the manifest records it.ModifiéFonctionneA native crawl now measures its own reading level.#1516
The new
#1516 (ticket sur GitHub)checks.readabilitymodule computes, offline over each page's retained content area, the sentence count, average words per sentence, the Flesch Reading Ease score (206.835 − 1.015·words/sentences − 84.6·syllables/words, English vowel-group syllable heuristic, versionflesch-reading-ease.v1) and the standard band label. The four values are stored as per-URL scan fields (pages.sentence_count,avg_words_per_sentence,flesch,readability), carried into the analyzer frame under the same column names an SF export uses — so the existing READABILITY_DIFFICULT and LONG_SENTENCES checks now fire on native crawls too, with no new check IDs. A page whose content area was not retained, or holds no word with a letter, records NULL/"" — unmeasured, never a zero that would read as measured-and-easy. Text ratio and word count behavior are unchanged.CorrigéFonctionneCrawls on hosts that enforce https no longer spend a redirect hop on every http URL.#1484
The new opt-in
#1484 (ticket sur GitHub)http.upgrade_to_httpssetting rewrites http URLs to https before any request — seed, sitemap seeds and discovered links alike, in both spider and list modes and in native scans: no redirect is recorded for the upgrade, the http and https spellings collapse into one page identity, and each page fetched this way carriestransport_upgraded: truein its record (persisted aspages.transport_upgraded, absent in older scans reads as false). The operator opting in is the declaration that the hosts enforce transport security; a host without https answers with visible connection errors rather than a silent fallback. With the setting off, nothing changes.ModifiéFonctionneA crawl started deep can now stay in its own subtree.#1481
The new
#1481 (ticket sur GitHub)scope.start_folder_onlytoggle admits only URLs at or below the start URL's path folder: a crawl started at/blog/postwalks/blog/and everything under it, and links pointing above the folder are counted as unfollowed evidence (excludedunderoutside_start_folder) exactly like any other scope rejection — no hand-written include patterns required. The folder is the directory the start URL sits in (a trailing-slash start keeps its own folder); a start URL at the host root is refused, because the root has no folder to stay below. The setting is recorded per scan in the run manifest like every result-affecting setting, and appears incrawl-describe-settings. With the toggle off nothing changes.CorrigéFonctionnePages that link at robots-disallowed targets are no longer silent about it.#1486
When robots.txt disallows a linked URL and the crawl's robots policy is
#1486 (ticket sur GitHub)respect, the target is never fetched and never becomes a page row, so no target-side check can see it: the link between a page and a whole hidden section existed only in the edge list. The newROBOTS_BLOCKED_OUTLINKfinding (notice) lists, per source page, the outlinks whose destinations the policy kept out — both URLs named, internal and external edges alike, the target still never fetched. The population follows the robots policy mode: underreport_onlythe same targets are reported (they would have been blocked), and underignorethe check skips honestly — no URL was measured against robots.txt. Crawl-list runs without retained link edges skip by name like the other link findings.ModifiéFonctionneAn image-only link now names its destination when the wrapped image carries alt text.#1518
Links whose visible content is an image used to show an empty anchor in link evidence even when the image had a descriptive
#1518 (ticket sur GitHub)alt, so anchor-text review treated every image link as unnamed. The parser now records that alt as the link's anchor with ananchor_source: "alt"marker distinguishing alt-derived anchors from text-derived ones (absent marker = text or nothing). A link with visible text keeps its text; a link with neither stays empty and keeps feeding the no-anchor-text checks. The marker rides the LinkEdge into the scan (links.anchor_source), the links export and the per-link query views; a scan written before the field reportsnullthere rather than a false text-derived answer.ModifiéFonctionneHrefs that cannot become fetchable link targets leave evidence instead of vanishing.#1530
Link parsing drops an unparsable href (malformed syntax, e.g. a truncated
#1530 (ticket sur GitHub)http://[invalid) or a non-http(s) scheme (mailto:,javascript:) with no trace: the source page keeps nothing to say it points at something broken. The new opt-in crawl settingdiscovery.record_invalid_targetsrecords each rejection per page — the raw href exactly as written plus the reason (unparsable_syntax/unsupported_scheme) — through the parser, the crawl record, the scan schema (pages.invalid_link_targets_json) and the analyzer frame, and the newINVALID_LINK_TARGETcheck turns the pages that have them into findings with the raw values as evidence. A missing href and a pure in-page#anchorare absence, not defects, and are never recorded; a measured clean page records an empty list; a crawl that ran without the setting skips the check honestly instead of reporting a clean site nobody measured. Rejected targets are data: they never enter the frontier. The setting is off by default and is recorded in the run manifest like every finding-changing setting.ModifiéFonctionne
HEADING_SKIPnow flags heading-order jumps in both directions, completing the#1545heading-sequence check: a content heading that skips a level down (H2 -> H4) and one that jumps back up past an open section (H4 -> H2) are the same defect -- the outline steps over a section that was never opened or never closed. A one-level return (H3 -> H2) closes the current section and starts a sibling, which is correct nesting and stays clean, matching the SF 24.3
#1545 (ticket sur GitHub)axe.heading_orderbehaviour the check was modelled on. Evidence rows carry from/to levels and texts for both directions.ModifiéFonctionneThird-party product names now appear only where they describe compatibility: a trademark#1554
and non-affiliation notice was added, the export analyzer is called Export Analyzer, and report reasons and docs describe what an export lacks instead of making claims about another product.
#1554 (ticket sur GitHub)
21 octobre 2026
3 ajouté4 modifié3 corrigéModifiéBêtaApplication de bureau : le jeu de données d'URL en liste plate ou en arborescence de#1492
chemins.
Un bouton liste/arborescence en bas de la barre des groupes fait basculer le jeu de données entre la page plate et une arborescence de chemins repliable, construite sur les mêmes lignes et les mêmes filtres : les hôtes sont les nœuds de premier niveau, les URL de répertoire sont leurs propres dossiers, et les URL de documents se rattachent au dernier dossier. Sélectionner une feuille pilote la sélection partagée du tableau : les détails et les filtres sont conservés lors du changement.
#1492 (ticket sur GitHub)AjoutéBêtaApplication de bureau : mode relecture des données seules.#1495
Affichage > Données seules (Ctrl+Alt+D) masque la navigation et la barre supérieure pour les longues sessions de relecture et ne laisse que les données. Ce choix est conservé pendant la session, et les panneaux de chaque écran continuent de répondre à leurs propres bascules.
#1495 (ticket sur GitHub)CorrigéBêtaLogs : accepter le temps de réponse en toute position et les formats à un seul ident.#1534
Les analyseurs Apache extraient désormais le referer, le user-agent et le time-taken lorsque le temps de réponse se trouve entre la taille et le referer, ou en fin de ligne, au lieu d'ignorer silencieusement ces deux champs entre guillemets. La détection du format accepte un ou deux jetons ident entre l'IP du client et l'horodatage : le format nginx personnalisé à tiret unique s'importe désormais au lieu d'échouer en bloc. Le rapport d'import nomme les variantes acceptées (combined-2-ident, combined-1-ident). Corrige #1534 et #1535.
#1534 (ticket sur GitHub)ModifiéBêtaApplication de bureau : l'échelle d'espacement est terminée, plus aucune marge hors#1374
échelle.
Les 184 appels setContentsMargins/setSpacing restants hors échelle, répartis sur 60 fichiers, passent à l'échelle de jetons avec une correspondance qui ne fait que resserrer. Le test de cliquet (ratchet) échoue désormais sur toute valeur hors échelle. Recherche, images, boîte de réception, issues, logs, journal et intégration ont été normalisés en premier.
#1374 (ticket sur GitHub)ModifiéBêtaApplication de bureau : gains rapides, valeurs TSV brutes, projet dans le titre de la
fenêtre, texte tertiaire conforme AA.
copy_tsv colle les valeurs stockées, si bien que le texte d'espace réservé « pas de données » n'atteint jamais un tableur ; le titre de la fenêtre affiche le projet ouvert (Projet — SpiderHead) ; le texte tertiaire respecte désormais le contraste AA sur les surfaces surélevées.
CorrigéBêtaApplication de bureau : régions clavier honnêtes, Entrée ouvre les détails, sources
d'erreur lisibles.
F6 parcourt uniquement la navigation et la page courante. Entrée ouvre les détails de la ligne sélectionnée, comme le promettait toujours le menu de ligne. Les erreurs de lecture dans la barre d'état nomment l'opération (tableau d'URL, paramètres de crawl) au lieu de l'identifiant brut de la requête.
ModifiéBêtaApplication de bureau : le menu Affichage se lie aux panneaux propres à l'écran courant.
Un écran peut exposer panel_actions(), c'est-à-dire des triplets (titre, affiché, bascule), et le menu Affichage construit sa section des panneaux à l'ouverture de l'écran. L'écran des URL propose son résumé de passage et le mode tableau seul ; les anciens éléments d'audit restent sur leur propre page.
CorrigéBêtaApplication de bureau : les ticks d'activité n'inondent plus les journaux des fenêtres
fermées.
Le signal global d'activité continuait d'appeler des widgets BusyIndicator supprimés. Les fenêtres fermées restent désormais silencieuses.
AjoutéBêtaApplication de bureau : les onglets du mode simple peuvent être masqués et restaurés.
Menu contextuel de la barre d'onglets : masquer un onglet, restaurer les onglets masqués, tout afficher. Le choix est conservé. Le premier onglet reste toujours, le dernier onglet visible ne peut pas être masqué, et un onglet masqué réapparaît lorsque la Vue d'ensemble y mène.
AjoutéBêtaApplication de bureau : états du mode simple, barre de marque et captures hors ligne.
Panneaux d'état avec actions : aucun résultat (effacer la recherche / tout afficher), échec (réessayer / afficher le journal), noyau introuvable (ouvrir les paramètres). Un avis d'arrêt compte les URL explorées et en file d'attente. La barre d'outils porte le logo de marque (une araignée), et le compteur d'erreurs est un badge.
20 octobre 2026
1 ajouté3 modifiéModifiéBêtaLe paquet CLI
seoheadest découpé en modules par domaine.#1514
#1514 (ticket sur GitHub)cli/_shared.pycontient les registres de commandes et les aides de saisie.checks_cmds,project_cmds,scan_cmds,crawl_cmds,sources_cmds,reports_cmdsetmisc_cmdsportent chacun leur tranche de l'ancien mapping d'arguments nommés decli/__init__.py, des options argparse et des cas particuliers du dispatch principal. La surface d'import publiée est inchangée et le module passe de 4 506 à 513 lignes. La sortie de--helpreste identique octet pour octet pour chaque commande.ModifiéBêtaLes handlers MCP passent dans un paquet dédié.#1514
Les handlers du serveur vivent désormais dans
#1514 (ticket sur GitHub)seohead/mcp/handlers/, répartis par domaine : projets, trafic, logs, sources, contenu, sémantique, workflow, scans conservés, preuves et export BI, environnement et catalogue. La documentation et les références aux chemins des sources suivent la nouvelle structure.ModifiéBêtaTableau d'URL : sélection de plusieurs lignes avec copie TSV de la sélection.#1347
Finition du tableau d'URL de #1347 : sélectionnez plusieurs lignes et copiez exactement la sélection au format TSV.
#1347 (ticket sur GitHub)AjoutéBêtaNoyau de contrôle du débit de crawl en direct (en cours).#1360
Le noyau qui permet de modifier le débit du crawl pendant qu'un scan tourne (#1360). Le câblage dans l'application de bureau suit.
#1360 (ticket sur GitHub)
Jours plus anciens (4)Show all
14 octobre 2026
3 modifiéModifiéBêtaApplication de bureau : progression du scan, limite d'URL, bouton d'arrêt et motifs de#1358
fin.
Traitées, en file, vitesse et ETA, ainsi que la limite d'URL demandée, s'affichent dans la liste des scans, le détail du scan et la carte de navigation. Le bouton d'arrêt de la barre supérieure s'intitule « Arrêter le scan » au lieu d'un identifiant d'exécution, se met à jour sans scintillement et disparaît une fois l'exécution terminée. Les exécutions finies indiquent pourquoi elles se sont arrêtées (limite d'URL, de requêtes ou de temps, erreurs, disque).
#1358 (ticket sur GitHub)ModifiéBêtaApplication de bureau : un seul système de boutons, marges de page standard, sans puces
indisponibles.
Un seul système de boutons sur tous les écrans, des marges de page standard, et les puces des fonctions indisponibles sont retirées.
ModifiéBêtaApplication de bureau : le générateur de redirections et l'éditeur de snippets passent
dans une section Outils.
Méthodes ne contient plus que le catalogue des méthodes et affiche le scan sélectionné à l'ouverture. L'éditeur de snippets présélectionne le contexte du scan sélectionné.
13 octobre 2026
3 modifiéModifiéFonctionneCrawler de bureau : onglets de détail d'URL (liens entrants, sortants, ressources,#1349
snippet, source enregistrée, en-têtes) sur le fichier de crawl, filtres par onglet avec compteurs, vue d'ensemble plus complète avec les directives, sélecteur de colonnes par onglet relié aux colonnes d'export, tri par en-tête, menu contextuel et navigation au clavier. Nouveau scan peut enregistrer ses réglages comme profil de crawl nommé.
#1349 (ticket sur GitHub)ModifiéFonctionneSurfaces mortes du bureau : la section Graphe de liens lit les totaux de nœuds, d'arêtes#1347
et d'orphelines, et une page…
Surfaces mortes du bureau : la section Graphe de liens lit les totaux de nœuds, d'arêtes et d'orphelines, ainsi qu'un tableau paginé de nœuds, depuis la commande
#1347 (ticket sur GitHub)scan-graphdu noyau, au lieu d'un espace réservé. Problèmes liste les plus gros groupes de contrôles à partir des compteurs par contrôle du noyau. Le tableau d'URL affiche et trie la colonne Liens entrants, avec un repli et une note lorsqu'un scan n'a conservé aucune observation de liens ; la colonne Nombre de problèmes, jamais mesurable, n'est plus proposée. Export et rapports exécutent la commandescan-exportdu noyau pour les pages ou les constats, en CSV, XLSX, JSON ou XML, dans un dossier choisi. Les jeux de données de comparaison et de tâches, la colonne d'espace réservé et l'historique des exports que le noyau ne peut pas soutenir sont supprimés. Les réglages stockés mais pas encore appliqués sont désormais visiblement désactivés, avec la raison au survol. Le dialogue de configuration du crawl liste une fois, en lecture seule, les paramètres sans éditeur validé, au lieu de les mélanger à la liste de l'éditeur, et le champ « Pourquoi ce scan » du Nouveau scan explique pourquoi il est vide.ModifiéFonctionneSurfaces mortes du bureau : la section Crawler lance désormais un crawl ad hoc sans#1347
projet. Le champ d'adresse, le sélecteur Site/Liste/Sitemap, la limite d'URL, Démarrer/Arrêter, les onglets de contrôle, la recherche d'adresse, les compteurs de la vue d'ensemble, le volet de détail et l'export CSV/XLSX sont reliés aux commandes du noyau
#1347 (ticket sur GitHub)crawl-site --scan-outetscan-url-query. La barre d'état affiche la vitesse, les pages explorées et les pages restantes fournies par le noyau. Rien ne démarre à l'ouverture de l'écran, et le résultat temporaire est supprimé à Effacer, au crawl suivant et à la fermeture de l'application. Les entrées du menu de crawl rapide ouvrent la section. Le badge « Indisponible dans cette version du noyau » de la barre supérieure est retiré. La comparaison n'affiche plus d'état d'erreur après une lecture de projet : une comparaison vidée est une invite neutre pour choisir deux scans, et seul un échec réel affiche l'état indisponible.
12 octobre 2026
8 ajouté10 modifié2 corrigéCorrigéFonctionneCorrection : compatibilité de la configuration de scan natif lors des mises à niveau et#1344
des retours arrière.
Correction de la compatibilité de la configuration de scan natif lors des mises à niveau et des retours arrière. Préserve l'en-tête et l'empreinte existants, conserve les nouvelles options effectives dans un contexte versionné, et garde le comportement historique des paramètres de suivi lorsque le contexte est absent. La reprise valide l'ensemble des réglages effectifs et refuse les artefacts non pris en charge ou incohérents.
#1344 (ticket sur GitHub)CorrigéFonctionneCorrection de la détection RDFa conservée : les IRI de propriétés relatives et les hôtes#1344
non liés n'établissent plus de balisage Schema.org. Les IRI de vocabulaire absolus et les préfixes reconnus sont préservés, et les tests de délai de recherche restent bornés grâce à des motifs de fixtures peu coûteux.
#1344 (ticket sur GitHub)ModifiéFonctionneGarder les onglets compacts des paramètres de projet dans la fenêtre, et exposer les#1315
réglages supplémentaires via…
Garder les onglets compacts des paramètres de projet dans la fenêtre et exposer les réglages supplémentaires via un menu de débordement. Masquer le sélecteur de planification redondant lorsque seul le site principal est disponible, tout en conservant la sélection entre sites liés, quelle que soit la taille de la fenêtre. - Afficher l'aperçu d'un User-Agent personnalisé seulement lorsque sa valeur est disponible ; ne pas présenter le libellé d'un préréglage comme l'en-tête réellement transmis. Préserver les limites de délai configurées du planificateur. - Réévaluer la création de tâches du propriétaire après l'actualisation du projet, et conserver les exécutions concurrentes validées par le noyau malgré les révisions de checklist sans rapport. La confirmation et les vérifications de révision courante restent obligatoires pour les écritures. - Désactiver la vérification de bot tant que les journaux de projet conservés ne sont pas disponibles, prendre en charge les appels ordinaires au modèle de table Qt, et afficher séparément les totaux d'incidents enregistrés, les échantillons d'URL bornés et les contrôles non mesurés. - Enregistrer les polices embarquées une seule fois par application, au lieu de les enregistrer en double à chaque changement de thème.
#1315 (ticket sur GitHub)AjoutéFonctionneAjout de la gestion des identifiants fournisseurs en écriture seule via le canal stdin de#1343
la CLI locale, consentement GSC explicite…
Ajout de la gestion des identifiants fournisseurs en écriture seule via le canal stdin de la CLI locale, d'un cycle de vie explicite du consentement OAuth GSC, et de la découverte de ressources de compte en aperçu seulement. Le choix du fournisseur et les écritures d'identifiants MCP restent soumis à des décisions explicites du propriétaire. - Ajout de constats bornés d'Inspection d'URL dans la sortie SF liée au projet, derrière l'option explicite
#1343 (ticket sur GitHub)--gsc-inspect, avec comptabilisation locale du quota par propriété et historique d'inspection conservé et filtré par liste blanche. SF joint aussi l'historique d'URL mesuré de GSC, GA4, Metrika et Cloudflare, sans appel implicite de fournisseur. Les constats par URL enregistrés entrent dans le flux ordinaire des problèmes d'audit, tandis qu'une couverture manquante ou partielle est signalée explicitement. - Ajout d'instantanés quotidiens agrégés par URL pour Cloudflare, d'import Logpull Enterprise explicite, d'import de NDJSON Logpush fourni par l'appelant via le magasin de logs local, et de lectures en lecture seule des réglages IA, des jeux de règles de redirection et WAF, et des tâches Logpush, activées par choix. Une capture Instant Logs, confirmée séparément et soumise au plan, utilise une courte session WebSocket bornée et ne conserve qu'un préfixe local plafonné. La route laisse intactes les sessions existantes et signale l'expiration par inactivité documentée par Cloudflare, car l'API Edge n'expose aucun point d'arrêt. Les flux des destinations Logpush restent la propriété du client et ne sont pas ingérés. - Ajout d'une opération explicite de backlinks JSON-RPC Moz actuelle, les métriques d'URL héritées restant le comportement par défaut, et conservation à null des reçus de dépense incertains dans les rapports du journal. - Ajout d'un lecteur borné du résumé des artefacts PageSpeed, qui valide la propriété et ne renvoie que les champs Lighthouse autorisés.ModifiéFonctionneLes profils de crawl nommés peuvent être inspectés en JSON validé, sélectionnés via#1299
#1299 (ticket sur GitHub)seo_crawl_site, et attribués comme valeurs par défaut du projet, les surcharges de crawl du projet se superposant par-dessus. - L'estimation de progression du crawl calcule le temps pour la frontière connue en cours et l'indique séparément de la durée totale du crawl. Les cibles de crawl normalisent par défaut les paramètresutm_*etfbclid. - Les scans natifs actifs du projet acceptent des demandes ponctuelles de pause et d'arrêt, par identifiant d'exécution et point de contrôle, via le chemin de reprise SQLite existant. - L'authentification HTTP Basic/Digest liée à l'hôte et la négociation zstd facultative des réponses utilisent des références d'environnement, un accord explicite, une authentification en HTTPS uniquement et des budgets de requêtes. Les résultats de crawl incluent des estimations de charge bornées et conservent une taille de disque inconnue comme null.AjoutéFonctionneAjout d'un résumé opérationnel de l'état du projet pour la progression active du#1291
collecteur, la complétude du dernier scan…
Ajout d'un résumé opérationnel de l'état du projet : progression active du collecteur, complétude du dernier scan, santé des dossiers, tâches ouvertes du projet principal, dernière activité d'agent enregistrée et taille persistée du dossier de scan. L'état de connexion reste inconnu sans battement de cœur en direct.
#1291 (ticket sur GitHub)AjoutéFonctionneAjout d'un contrat d'infos du noyau lisible par machine : révision et date de build#979
vérifiées, outils CLI et MCP pris en charge, et compatibilité de l'application de bureau à la révision exacte. Ajout d'une commande
#979 (ticket sur GitHub)doctoren lecture seule et d'un outil MCP correspondant, pour vérifier les prérequis locaux.seohead --versionaffiche la version du paquet. - Ajout de lectures conditionnelles opaques tenant compte de la révision pourproject-observeetproject-activity. Ajout de vérifications de version soumises à consentement et de téléchargements de wheel et de source vérifiés par somme de contrôle, avec progression, annulation et aucune installation automatique.AjoutéFonctionneAjout d'estimations de pré-contrôle des étapes payantes et d'un historique de statut par#1298
étape aux projets semantic-core. - Prise en charge des aperçus d'import CSV/XLSX mappés, des exports limités, de la lecture paginée des clusters, des sélections de page de destination (landing) auditées et d'insights locaux fondés sur des preuves.
#1298 (ticket sur GitHub)AjoutéFonctionneAjout d'actions de page et de préréglages bornés, activés par projet, aux crawls rendus,#1302
avec différences d'éléments brut/rendu conservées…
Ajout d'actions de page et de préréglages bornés, activés par projet, aux crawls rendus, de différences d'éléments brut/rendu conservées et d'échecs réseau du navigateur. Exposition de constats de débordement mobile, de taille de texte et de taille de cible à partir des mises en page rendues et capturées. Ajout d'une capture d'écran de la première vue et de métadonnées explicites sur la représentation du DOM.
#1302 (ticket sur GitHub)AjoutéFonctionneAjout des états exacts
checks_run,checks_skipped,checks_disabledet du total du#1296registre dans les preuves des scans de projet enregistrés. Les compteurs évalués et ignorés restent disjoints, et une couverture d'audit manquante est signalée comme indisponible. - Les scans enregistrés exposent des fenêtres HTML statiques et rendues bornées, avec identité du scan et du document, empreintes du corps brut et du texte décodé, et pages de constats pour l'URL exacte, via les gestionnaires CLI et MCP partagés. Le détail d'URL inclut une fenêtre bornée du HTML source. Les preuves indisponibles ou ambiguës restent explicites. La comparaison de corps prend en charge les paires statique/rendu ainsi que les paires entre scans. - Le statut du scan indique l'état d'intégrité, l'identité du scan concerné, la disponibilité des points de contrôle et des compteurs de dommages nullables. Les archives de projet portables peuvent omettre tous les corps HTML, omettre les corps plus anciens que 30 ou 90 jours, ou ne conserver que les N scans reconnus les plus récents, tout en préservant les tables de preuves.
#1296 (ticket sur GitHub)ModifiéFonctionneRequêtes d'URL sur scans enregistrés étendues : nombre de liens conservés, filtres#1295
titre/canonical/hreflang/sitemap, et facettes tenant compte de la source. Les mesures absentes de lien, de sitemap, d'hreflang ou d'audit restent indisponibles au lieu d'apparaître comme zéro. - Ajout de compteurs optionnels de sévérité et par contrôle pour l'ensemble du scan, issus du résumé d'audit enregistré, avec des états explicites par contrôle : déclenché, silencieux, ignoré, désactivé et indisponible. - Un index reconstructible de profondeur de clic le plus court est persisté à la fin d'une capture scan.v2 ou d'une réanalyse hors ligne. Les anciens fichiers de scan continuent de dériver la profondeur de leur graphe de liens conservé, dans le budget de requête. - Ajout de filtres de type et d'URL de ressource liés au document source, de compteurs d'occurrences et par type, d'états d'octets conservés, et d'agrégats de blocs et de poids dans les preuves de ressources enregistrées. Les déclarations indisponibles et les poids de récupération partiels restent explicites. - Capture dans scan.v2 des durées HTTPX TCP/connexion, TLS, attente des en-têtes de réponse et téléchargement du corps, lorsque le transport émet des événements de trace. Le DNS reste indisponible, car HTTPX n'expose aucun événement de résolution distinct ; son temps peut être inclus dans la connexion. Ajout de
#1295 (ticket sur GitHub)headers-analyzehors ligne pour les en-têtes expurgés et les preuves de phases enregistrés. - Ajout d'une lecture CLI/MCP de l'arborescence de répertoires via une agrégation SQLite temporaire, avec profondeur, nombre d'enfants, nombre de nœuds et taille de sortie bornés. Les lectures de chemin le plus court existantes restent disponibles viascan-link-inspect --view path. - Ajout descan-link-inspect --view links --preset inlinks|broken_linkspour les vues nommées des liens entrants internes et des cibles sortantes cassées. - La finalisation native scan.v2 et la réanalyse hors ligne tentent désormais une projection indexée facultative des requêtes d'URL, liée à la source, pour le nombre et les types de ressources des pages, les indicateurs de titre en double, la couverture et les facettes sans filtre. Les lectures ne l'acceptent que pour un scan terminé dont l'UUID, la révision des preuves, les capacités et l'URL de départ correspondent ; une remise en file invalide sa publication. Les anciens scans gardent le repli borné en lecture seule, sans migration scan.v1. Sur le jeu synthétique d'un scan terminé de 1 M de pages, avec HTML et images partagés, le producteur de projection réel a fini en 6,36 s, à 400,7 Mo de RSS maximal. Les médianes de cinq passes étaient de 5,23 à 43,38 ms pour les pages avec colonnes de ressources, 60,81 ms pour le filtre d'images et 5,38 ms pour toutes les facettes, dans les budgets de 200/300 ms ; 100 lignes d'images étaient exactes. Cela valide la performance synthétique des requêtes enregistrées, pas un crawl en direct de 1 M d'URL. Les facettes d'inlinks et de sitemap sont restées explicitement indisponibles, si bien que la réponse des facettes était partielle.ModifiéFonctionneLa checklist de projet v4 porte les assignés, les critères d'acceptation, les liens vers#1294
constats et méthodes, bornes…
La checklist de projet v4 porte les assignés, les critères d'acceptation, les liens vers constats et méthodes, des plafonds bornés d'URL concurrentes et des commentaires de tâche attribués en ajout seul, tout en continuant de lire les formats de couverture antérieurs. - L'acceptation d'un objectif de la boîte de réception crée sa tâche de checklist personnalisée et stable, avec l'objectif d'origine comme description. L'acceptation n'exécute ni ne termine la tâche. - Les vues de tâches et de rapports exposent des états de mesure honnêtes des URL touchées par tâche, ainsi que des KPI de tâches personnalisées. Les compteurs restent indisponibles lorsque les preuves conservées n'énumèrent pas les URL touchées.
#1294 (ticket sur GitHub)ModifiéFonctionneLes rapports Markdown, DOCX et XLSX liés au projet incluent les compteurs de tâches#1297
personnalisées, l'assignation et l'acceptation…
Les rapports Markdown, DOCX et XLSX liés au projet incluent les compteurs de tâches personnalisées, les champs d'assignation et d'acceptation, ainsi que la preuve nullable des URL touchées avec son état de mesure.
#1297 (ticket sur GitHub)AjoutéFonctionneAjout de métadonnées de catégorie et de rattachement au projet, avec pagination par#1312
décalage, au catalogue d'outils MCP, et d'un lot borné de vérification d'URL pour des listes explicites ou des pages de scan conservées. - Ajout d'une qualité d'image automatique guidée par SSIM, d'un retrait explicite des EXIF et de rapports d'optimisation d'images sur les scans conservés. Ajout d'une sortie CSV de redirections WordPress Redirection, avec analyse locale des chaînes et des boucles. - Affichage dans l'aide des anciennes options de profondeur et de délai de crawl, en conservant leur comportement de compatibilité.
#1312 (ticket sur GitHub)AjoutéFonctionneLectures de flux compare.v2 bornées avec contrôles d'intégrité, tri et filtrage par URL ;#1300
production d'un résumé Markdown borné et de fiches avant/après par URL exacte, qui conservent les observations manquantes comme « non revérifiées ». - Extension de l'historique d'URL enregistré avec les faits de page et les deltas entre scans adjacents ; les observations indisponibles ou absentes restent non comparables. - Persistance des points de contrôle de comparaison et des instantanés de progression pour une reprise à entrée identique, réutilisation des paquets terminés vérifiés comme succès de cache, et ajout de tranches de segment principal par changement ainsi que de preuves de codes de réponse des liens externes conservés.
#1300 (ticket sur GitHub)ModifiéFonctionneExtension du lecteur de données structurées conservées par URL exacte avec JSON-LD borné,#1304
Microdata, RDFa, preuves de langue, traçabilité des sources et validation Schema.org hors ligne et locale.
#1304 (ticket sur GitHub)ModifiéFonctionneLa recherche de contenu conservée signale désormais des occurrences bornées, avec#1301
décalages de caractères dans le texte source, lignes numérotées à partir de 1, navigation de correspondance par document et, facultativement, fenêtres expurgées de six lignes. Le filtrage par URL exacte s'exécute avant que SQLite ne classe les documents conservés. Les comptes complets de correspondances restent visibles lorsque la limite de 10 000 décalages ou de 64 Kio par enregistrement tronque les occurrences renvoyées, et la couverture est alors partielle. Les recherches par expression régulière sont indisponibles lorsqu'un délai mural ne peut pas être appliqué. Les corps indisponibles restent inconnus, jamais absents. - Les paquets de recherche indexés sont plafonnés à 512 Mio ; en atteignant la limite, un paquet explicitement incomplet est publié, avec sa couverture mesurée et la limite d'octets. - Ajout d'une route hors ligne d'échantillon d'extraction personnalisée pour du HTML fourni par l'appelant. Son URL facultative n'est qu'une étiquette et ne déclenche aucune récupération ; la récupération d'URL en direct reste une décision du propriétaire.
#1301 (ticket sur GitHub)ModifiéFonctionneImporter les lignes CSV de modification de snippets dans des tâches de relecture non#1010
appliquées, mesurer la typographie locale selon un profil fourni par l'appelant, et ajouter des extraits de requêtes issus de scans conservés explicitement sélectionnés, avec traçabilité du document. Aucun seuil de moteur de recherche ni affirmation sur le rendu n'est inférée.
#1010 (ticket sur GitHub)ModifiéFonctionneLes crawls natifs enregistrés comparent les corps de réponse conservés avec un ensemble#1013
borné de familles Content-Type reconnaissables. Les corps indisponibles, trop volumineux ou ambigus restent explicitement non mesurés.
#1013 (ticket sur GitHub)ModifiéFonctionneL'analyse d'images native conservée signale les déclarations de taille manquantes ou#1303
invalides, ainsi que les alt vides informatifs ou mal formés…
L'analyse d'images native conservée signale les déclarations de taille manquantes ou invalides, ainsi que les textes alternatifs vides informatifs ou mal formés, tout en préservant les
#1303 (ticket sur GitHub)alt=""décoratifs valides. Les images dotées d'un attribut alt littéralement absent restent des constats avec un nom ARIA ; les images décoratives visibles ont encore besoin de dimensions. - Les constats d'image conservent la portée source, cible et statut, la raison, le document et des emplacements ordinaux bornés. Les corps manquants préservent les faits positifs connus sur le nombre de pages, sans inventer d'URL d'image ni de réponse de ressource. Les erreurs de réponse d'image et les seuils d'octets conservent statut et raison. - La sélection d'images résout les pages sources après réouverture. Les preuves de ressources nomment les lignes conservées de récupération et de déclaration aux côtés des documents sources. L'identité native d'une image inclut la page source et la portée, ce qui empêche volontairement la fusion entre pages, et migre les anciennes empreintes d'images natives. Les anciens constats sans page source conservent leur identité précédente. Les éléments répétés gardent des comptes exacts et des échantillons d'emplacements bornés. - Le magasin synthétique (shop) conserve les défauts d'origine et 15 contrôles, et ajoute des cas de texte alternatif vide informatif et de taille manquante sur des produits existants, sans modifier les comptes de crawl.
11 octobre 2026
16 modifiéModifiéFonctionneExécuter la suite complète de l'outillage une seule fois sur Python 3.15 au lieu d'une
matrice de versions plus anciennes, en conservant quatre groupes de tests vérifiés et une couverture combinée. Utiliser la même version stable de Python pour l'amorçage de la CI et des builds de publication.
ModifiéFonctionneConsolidation des contrôles d'audit conservés et des améliorations natives du bureau.
Préserve les réglages de budget de ressources sans modifier l'en-tête figé du scan, conserve la réanalyse hors ligne et la reprise sûres, et partage les composants natifs des paramètres de projet, les jetons de thème et les polices embarquées. Retire les fichiers de conception privés de l'arborescence distribuée. - Lie les recontrôles de remédiation à la politique d'actifs effective validée, tout en préservant l'en-tête d'origine du scan et les octets source.
ModifiéFonctionneReconstruction des paramètres de projet Planification et Crawl avec des composants natifs#1323
partagés, Roboto hors ligne…
Reconstruction des paramètres de projet Planification et Crawl avec des composants natifs partagés (canvas), polices Roboto et Material Symbols hors ligne, et mise en page en colonne unique à 800 px. - L'interface de l'application reste entièrement en anglais, les contrats existants de commande de planification et de document sont conservés, et seules les données de planification adossées au noyau sont affichées. - Préserver les identités et alias des espaces de travail pendant que les paramètres du projet sont ouverts ; empêcher les enregistrements en double pendant que le noyau répond.
#1323 (ticket sur GitHub)ModifiéFonctionneLes paramètres de projet du bureau reçoivent un onglet Planification (jours de semaine,#1322
jours du mois, fenêtre d'exécution, fuseau horaire, concurrents) et un onglet Crawl (mode de crawl, préréglage User-Agent ou chaîne personnalisée, pause entre requêtes, budget d'URL, SpiderHeadBot ou un user agent personnalisé). Les deux lisent et écrivent le projet via le noyau, et l'aperçu du projet affiche la prochaine et la dernière exécution planifiée. Un mode --tray exécute les scans dus du noyau et ne maintient le Mac éveillé que pendant qu'un scan tourne.
#1322 (ticket sur GitHub)ModifiéFonctionneLes audits natifs exposent désormais le balisage de tête bloquant le rendu, à partir du#1303
HTML conservé complet, sans changer le schéma scan.v1 figé. Les corps manquants, incomplets ou limités par budget restent indisponibles. Les détections existantes de ressources de tête et l'entrée d'extraction personnalisée de SF sont réutilisées. - Ajout de budgets configurables d'octets pour la navigation observée et pour chaque ressource, de signaux de cache et de livraison des polices, d'heuristiques de minification et de scripts hérités, ainsi que de doublons exacts de scripts sur la même page, issus des preuves scan.v2 conservées. Les données non récupérées et les charges d'exécution incomplètes restent explicites. Les déclarations responsives ne sont pas additionnées comme poids d'exécution. Les bornes inférieures HTML seules, les compteurs de couverture scalaires, les échantillons de polices et les résumés de sources restent explicites et bornés. - Collecte de la couverture précise bornée JS/CSS de Chromium sur les rendus demandés, conservation de résumés d'usage UTF-16 et des constats de dimensionnement d'images rendues, puis lecture après réouverture ou réanalyse hors ligne. Les moteurs non pris en charge et les mesures manquantes ne deviennent pas des résultats propres. - Acceptation de JSON Lighthouse par URL, borné et correspondant, pour la découverte du LCP, les arbres de dépendances, les coupables de décalage de mise en page, la livraison d'images, les reflows forcés et les constats d'exécution des scripts, avec provenance de capture et empreintes des entrées. Aucun processus Lighthouse, fournisseur payant ni inférence de CWV terrain n'est ajouté. Mise à jour du registre, de la couverture SF et de la documentation de référence générée. - Les seuils d'actifs additifs restent hors de l'en-tête de scan natif figé, et leurs valeurs exactes sont stockées dans un contexte versionné et validé. La reprise compare ces valeurs séparément, et la réanalyse hors ligne les restaure depuis les preuves conservées. Les anciens lecteurs légers peuvent toujours inspecter la forme inchangée de la configuration d'en-tête.
#1303 (ticket sur GitHub)ModifiéFonctionneConservation des preuves AMP statiques et d'accessibilité liées au document dans les#1304
lignes de contexte de scan existantes, en préservant le schéma SQL
#1304 (ticket sur GitHub)scan.v1figé. Les constats AMP structurels inspectent le doctype d'origine, la structure head/body, le charset UTF-8, la viewport, le script d'exécution et les déclarations de boilerplate. Les cibles AMP appariées sans marqueur de racine AMP sont identifiées séparément. Les règles complètes du validateur AMP restent indisponibles dans l'attente d'une décision de licence. - Démarrage des constats d'accessibilité sur la langue du document, les étiquettes de formulaires, les alternatives d'images, les sauts de titres et le repère principal. Les constats préservent des exemples bornés et les comptes complets d'occurrences. Le contraste calculé et les règles axe-core rendues restent non mesurés. - Validation des microdonnées Schema.org et des sujets et propriétés RDFa courants, avec le vocabulaire fourni et les règles existantes de résultats enrichis. Les erreurs, avertissements et propriétés obligatoires manquantes conservés par URL deviennent des constats natifs. Les constructions RDFa non prises en charge constituent une preuve partielle explicite, et non un résultat de validation propre. - Service des arbres JSON-LD analysés, avec pour chaque bloc la source, la position et les erreurs de syntaxe, dansscan-structured-blocks; plafonnement des arbres de propriétés larges et exposition de leur limite. Lecture du dernier document pour une URL et une représentation demandées, sans modifier son scan. - Préservation des preuves structurées partielles ou indisponibles, sans masquer les erreurs confirmées des voisines. Résolution des identités JSON-LD prises en charge par rapport à la même base de document que le balisage HTML, tout en préservant le JSON source. Rejet des blocs JSON scalaires ou vides comme graphes valides, et partage de la sélection normalisée des balises JSON-LD vivantes entre l'analyseur et les projections servies.ModifiéFonctionneConservation des attributs canonical et de pagination bruts comme contexte de document#1305
versionné, sans modifier le SQL de scan figé. Les liens canoniques dotés d'attributs hreflang, lang, media ou type non pris en charge, les annotations mal formées et les déclarations inter-hôtes ou inter-protocoles produisent désormais des constats liés aux preuves. Les mots-clés de relation bien formés mais inconnus restent non classés. - Extension des sondes bornées d'URL cible du même hôte à la pagination, et conservation des observations mesurées de noindex et de canonical. Les chaînes canoniques natives et les constats noindex canonical/hreflang peuvent utiliser les sondes ; les anciennes sondes ne portant que le statut restent lisibles et non mesurées pour les directives HTML. - Jugement des chaînes de pagination natives opaques pour détecter les contradictions réciproques explicites next/prev, en conservant les règles numérotées ou par pas et les états nommés non jugés. Sélection des URL de constats enregistrées, y compris les membres groupés, via scan-url-query, à l'aide de tables SQLite temporaires et de preuves audit.v2 en flux. - Les jetons de relation Unicode inconnus et ceux contenant de la ponctuation restent non classés. Les autorités canoniques IDN et de port par défaut équivalentes sont normalisées, et les chemins cibles de pagination natifs exacts sont préservés. Les jetons de page mixtes illisibles restent non jugés. L'anomalie de pagination de la boutique est détectée même lorsque la page un n'a aucun numéro dans son URL. - Le rendu des annotations next/prev déclarées de la boutique fait que sa vérité terrain de pagination D41 est réellement servie et peut être validée par la capture native.
#1305 (ticket sur GitHub)ModifiéFonctionneLes journaux serveur conservent désormais des totaux exacts d'IP de bots sur disque au#1311
lieu d'un échantillon plafonné, avec des bornes…
Les journaux serveur conservent désormais des totaux exacts d'IP de bots sur disque au lieu d'un échantillon plafonné, avec des enregistrements par IP bornés, des visites, la dernière visite et des filtres par sous-réseau. La vérification DNS explicite contrôle séparément chaque IP sélectionnée, enregistre les preuves PTR et de résolution directe, prend en charge IPv6, et conserve les résultats indisponibles ou au délai dépassé comme non vérifiés. Les caches locaux expirent après une heure ; les lectures ne déclenchent jamais de DNS. - Ajout de l'import local des journaux de projet, du résumé paginé, des commandes de preuves de bots et de jointure avec les scans natifs, via les gestionnaires CLI/MCP partagés. Les artefacts de logs séparés conservent les partitions par date, la couverture et la provenance des sources, sans modifier les schémas de scan figés. Les preuves manquantes ou incomplètes retiennent les conclusions d'absence. Les écrans de logs du bureau font l'objet d'un suivi de présentation distinct.
#1311 (ticket sur GitHub)ModifiéFonctionneLes audits natifs conservés regroupent désormais les liens internes cassés par cible#1306
observée, avec preuves source/ancre…
Les audits natifs conservés regroupent désormais les liens internes cassés par cible observée, avec preuves de source et d'ancre, enregistrent les chaînes et boucles de redirection avec statuts par saut, distinguent la recherche interne basée sur le chemin et les ancres vides, et exposent les résultats externes sans réponse capturés. Les constats de cohérence du sitemap et du robots.txt utilisent l'appartenance conservée et les preuves de réponse, sans nouvelle récupération en direct ; les preuves manquantes ou partielles restent explicites. - Ajout d'un graphe de liens conservé et borné, avec filtres de position, de section et de voisinage local, agrégation par section et changements d'arêtes d'un scan à l'autre. La réanalyse hors ligne rétro-remplit les positions par lots dérivés reprenables, et signale les preuves non classées dans l'inspection des liens et le statut du scan, sans modifier le schéma scan.v1 figé. - Ajout d'une génération locale de sitemap XML à partir des pages 200 conservées, canoniques et indexables, avec lastmod observé, exclusions, découpage par protocole, index de sitemaps et sitemap d'images facultatif. La CLI et le MCP partagent les mêmes implémentations ; la publication exige un dossier local neuf et ne déploie jamais de fichiers. - Préservation des cibles de découverte manquantes, capturées au moment de la capture, lorsque le traitement ultérieur du sitemap ou des graines (seeds) ajoute l'URL au catalogue conservé. Les preuves expliquées de non-résolution ou d'exclusion restent immuables, tandis que les cibles connues revendiquées et les incohérences d'identité non nulles restent invalides. - Le sitemap d'images exclut les réponses de ressources non-200 connues, même lorsque la ressource n'a pas été stockée comme page explorée, et marque les réponses d'images non mesurées comme couverture partielle.
#1306 (ticket sur GitHub)ModifiéFonctionneContrôler les appels de production Google DataForSEO avant la construction du client,#1310
exposer des devis hors ligne…
Contrôle des appels de production Google DataForSEO avant la construction du client, exposition de devis hors ligne à partir des prix fournis, prise en compte de la profondeur SERP et des frais d'opérateurs de recherche, et application de plafonds connus en USD par appel avant tout transport. Extension des opérations bornées d'index de backlinks, et journalisation des coûts manquants ou des formes de réponse mal formées comme reçus incertains. Les limites de devise par projet et par mois restent en attente d'une décision du propriétaire. - Fusion des preuves explicites de source et d'import de backlinks dans des instantanés d'historique local privés, avec toutes les observations de jetons rel, les ancres, les domaines référents, les filtres et des signaux transparents de revue de spam. Ajout de routes CLI/MCP de profil et d'export de désaveu relu, sans soumission automatique. - Ajout de notices de revue des jetons rel mixtes et des mentions de sponsoring sortantes, issues des preuves natives conservées, en préservant le contexte indisponible, des comptes nuls pour les populations HTML entièrement non analysées, et les classes sponsored, ugc et nofollow dans les rapports et les contrôles de donneurs de liens, sans modifier le schéma figé des scans conservés.
#1310 (ticket sur GitHub)ModifiéFonctionneLes contrôles DOM et de doublons natifs lisent désormais le document conservé de chaque#1307
page par URL exacte…
Les contrôles DOM et de doublons natifs lisent désormais le document conservé sélectionné de chaque page par URL exacte, avec traçabilité vérifiée de la réponse et du corps. Les retenues désactivées, corps manquants, invalides ou dépassant le budget restent des omissions explicites ; les constats partiels portent une couverture. Les empreintes exactes de la zone de contenu et les constats SimHash/LSH bornés persistent dans l'audit natif, sans exiger de répertoire HTML SF. Les bandes LSH à seuil adaptatif préservent le rappel des paires qualifiantes tout en réduisant les gros seaux de candidats. L'épuisement du budget d'analyse proche de la similarité n'écarte pas les constats de hachage exact. - Les audits SF exécutent facultativement une similarité sémantique et une revue titre/H1 contre le corps conservé, avec un adaptateur local fourni explicitement ou des embeddings déclarés. Aucun modèle ni fournisseur n'est choisi automatiquement. L'identité de l'adaptateur, les empreintes source, les scores, les limites et les omissions accompagnent les candidats à la revue. La similarité sémantique n'affirme ni contenu dupliqué, ni pertinence de requête, ni cannibalisation. Les deux lignes de couverture SF sémantique restent des lacunes tant qu'un adaptateur réel n'est pas sélectionné et validé. L'autorisation d'un fournisseur externe exige le booléen littéral
#1307 (ticket sur GitHub)true; des chaînes ou des entiers jugés vrais n'autorisent jamais un appel d'embedding.ModifiéFonctionneLes références de la boîte de réception acceptent les identifiants d'objectif#1314
(
#1314 (ticket sur GitHub)inbox:<uuid>), afin qu'une note puisse citer l'objectif concerné. - Les éléments de checklist jamais tentés et manuels passent à l'étatnot_runau lieu dereview; seule une tentative en attente de décision reste enreview. - Le modèle de lecture Issues (preuvesobservedes constats) rapporte les comptes par contrôle (by_check) et unnext_offsetaprès les 20 premiers constats ;findings_pagepagine le reste.ModifiéFonctionneScans de projet planifiés (#1322, noyau) :
schedule.jsondans chaque dossier de projet#1322contient la planification propre à ce site…
Scans de projet planifiés (#1322, noyau) :
#1322 (ticket sur GitHub)schedule.jsondans chaque dossier de projet contient la planification indépendante de ce site (jours de semaine et/ou jours du mois, fenêtre horaire dans le fuseau du site, activation ou désactivation, mode de crawlspider/sitemap/list, budgets d'exécution par URL et par requête, délai de politesse, user agent, et état de reprise par tranche relatif au projet). Le projet propriétaire peut inclure les concurrents liés, chacun avec sa propre cadence et sa propre politique de scan. Les réglages se modifient viaschedule-set/seo_schedule_setavecexpected_revision;schedule-showindique l'état d'échéance du principal et des concurrents liés, ainsi que la prochaine exécution.schedule-run-dueest un point d'entrée sans interface qui n'exécute que les scans indépendamment dus, conserve des verrous à portée de processus jusqu'à la fin de chaque exécution, enregistre l'état d'annulation avant de la relancer, et journalise les résultats ou le chevauchement des concurrents dans chaque projet concerné.schedule-installetschedule-uninstalln'écrivent ni ne suppriment un plist launchd, une ligne cron ou une commande du Planificateur de tâches que lorsqu'ils sont appelés explicitement. - User agents de crawl : préréglages (spiderhead,googlebot,googlebot-desktop,googlebot-smartphone,bingbot,yandexbot,yandexmobilebot,baiduspider,duckduckbot,applebot,gptbot,claudebot,perplexitybot, et navigateurs de bureau et mobiles), ou une chaîne personnalisée. La valeur par défaut est désormaisSpiderHeadBot/<version> (+https://github.com/PavloSEO/spiderhead), et la correspondance du groupe robots.txt utilise la même identité. Les sites qui correspondaient à l'ancien groupe robotsSEOHEAD-Toolsdoivent nommerSpiderHeadBotdans leurs règles.ModifiéFonctionne
workflow-startn'a besoin que d'un objectif propriétaire accepté :--goal-id(ou#1314
#1314 (ticket sur GitHub)context.goal_id) suffit. Le prompt enregistré est par défautskill:workflow/full-audit-v1, et la tâche de checklist par défaut est celle créée à l'acceptation de l'objectif (custom:goal-<entry>). Les refus nomment la commande propriétaire qui corrige le lien manquant. Les options sont documentées dansseohead workflow-start --help. - Les populations de checklist acceptent des objets partiels : seulskindetsourcesont obligatoires, les autres clés prennent une valeur non définie par défaut. Une population refusée nomme ses clés manquantes ou inconnues. - Les crawls natifs de projet se rabattent sur/sitemap.xmlde l'origine lorsque robots.txt ne déclare aucun sitemap. Un repli absent est enregistré comme incomplet, mais n'interrompt pas le crawl.ModifiéFonctionneLes espaces de travail concurrents conservent l'URL telle que saisie : un crawl#1314
concurrent part de son URL
#1314 (ticket sur GitHub)wwwet non de l'hôte nu, et un crawl d'une URL concurrente ne marque plus les éléments de checklist du propriétaire ; le résultat n'est enregistré que dans l'espace de travail du concurrent. L'ouverture d'un projet dont l'espace de travail concurrent est endommagé signale ce concurrent comme indisponible, au lieu d'échouer.ModifiéFonctionneLa couverture de checklist de projet est désormais liée aux rapports audit.v2 en flux :
report-build --projectetaudit-workflow --action reportattachent l'instantané de checklist aux sorties xlsx, docx, csv, md et PDF, au lieu de produire un rapport non lié. Un scan arrêté par son budget d'URL (stopped_by_budget) est accepté comme dernier scan audité du projet, et son rapport indique la portée limitée.report-build --inputaccepte la cléformat.report-build --project --format jsonsur un audit en flux est refusé avec une explication, car le JSON est écrit tel quel et ne peut pas porter l'instantané de checklist.
Feuille de route
108 ouvertsJalons ouverts sur GitHub. Chaque ligne est un ticket ouvert : travail prévu, pas encore livré.
Les bases du bureau
D’abord une app de bureau qui marche : blocages, taille 800×600, onglets morts, états honnêtes (vague 1 du plan)
- AjoutéPrévuDesktop : parcours complet sur un projet réel (visuel, scans, problèmes, exports, outils)#1358 (ticket sur GitHub)
- CorrigéPrévuDesktop : onglets inactifs, actions désactivées et écrans provisoires#1347 (ticket sur GitHub)
AjoutéPrévuWindows : le crawl natif, le journal de projet et le build Desktop échouent ; Desktop#1345
multiplie les fenêtres vides et est inutilisable en 4K
#1345 (ticket sur GitHub)
Parité du bureau
Ergonomie au niveau attendu : onglets, colonnes par filtre, détail depuis la vue d’ensemble, mise en page conservée (épique #1522)
- AjoutéPrévuUn état d'activité unique partagé par toutes les fenêtres et commandes#1496 (ticket sur GitHub)
- AjoutéPrévuSchéma de clusters de contenu à partir des groupes de similarité#1460 (ticket sur GitHub)
Moteur d’exploration
Politesse et règles robots, relances, budgets, découverte, stockage à grande échelle, cycle de vie du scan (épique #1520)
AjoutéPrévuCollecter les annotations canonical et de pagination transmises dans l'en-tête de réponse#1517
HTTP Link, avec leur origine de déclaration
#1517 (ticket sur GitHub)- AjoutéPrévuPiloter un crawl en cours : restriction du périmètre à la volée et injection d'URL#1485 (ticket sur GitHub)
- AjoutéPrévuPlafond automatique du nombre de pages rendues en parallèle selon la mémoire disponible#1480 (ticket sur GitHub)
- AjoutéPrévuInclure le contenu du shadow DOM et des iframes dans la capture rendue#1477 (ticket sur GitHub)
- AjoutéPrévuCapturer les messages de la console du navigateur pour chaque URL rendue#1475 (ticket sur GitHub)
- AjoutéPrévuRésoudre les liens relatifs par rapport à l'URL de base du document#1470 (ticket sur GitHub)
- AjoutéPrévuDépenser le budget de crawl en commençant par les pages les moins profondes#1468 (ticket sur GitHub)
- AjoutéPrévuLa profondeur de clic reflète le chemin de découverte le moins profond#1467 (ticket sur GitHub)
- AjoutéPrévuNormalisation structurelle des URL avant la déduplication du crawl#1466 (ticket sur GitHub)
- AjoutéPrévuRécupérer robots.txt une seule fois par origine en cas de concurrence#1465 (ticket sur GitHub)
- AjoutéPrévuRespecter Retry-After et ralentir face aux réponses de limitation#1463 (ticket sur GitHub)
AjoutéPrévuBudgets de crawl étendus : profondeur de dossier, par sous-domaine, par niveau de#1445
profondeur, par motif de chemin, nombre max de liens par page
#1445 (ticket sur GitHub)AjoutéPrévuOptions de génération de sitemap : priority, changefreq, PDF, images CDN, fichiers par#1444
segment
#1444 (ticket sur GitHub)- AjoutéPrévuCertificats d'autorité personnalisés de confiance pour le trafic crawlé#1442 (ticket sur GitHub)
- AjoutéPrévuTraiter les noms d'hôte de CDN et de miroirs comme périmètre interne#1441 (ticket sur GitHub)
- AjoutéPrévuPolitique d'acceptation des cookies configurable pour les crawls#1438 (ticket sur GitHub)
AjoutéPrévuDécouverte à partir des cibles d'annotations (canonical, hreflang, AMP, meta refresh,#1436
iframes)
#1436 (ticket sur GitHub)AjoutéPrévucontrol/rate-and-load : distinguer les 5xx dus au débit des 403/429 de protection#1366
anti-bot à l'étape « avant d'accuser le site »
#1366 (ticket sur GitHub)AjoutéPrévurobots-check signale « pas de robots.txt (crawl autorisé) » quand robots.txt renvoie un#1364
403 d'un WAF
#1364 (ticket sur GitHub)AjoutéPrévuLe client HTTP reçoit un 403 à chaque requête vers une boutique Shopify derrière#1363
Cloudflare qui répond 200 à curl
#1363 (ticket sur GitHub)- AjoutéPrévuLive crawl speed control: change rate and threads during a running scan#1360 (ticket sur GitHub)
AjoutéPrévuCore : aspérités relevées lors de l'intégration de projets réels (concurrents, logs,#1359
limites de crawl, archive)
#1359 (ticket sur GitHub)
Analyse et champs de données
Lisibilité, vecteurs et sémantique, champs par URL, couverture des contrôles, synthèses (épique #1521)
- AjoutéPrévuConstats d'accessibilité avec choix du niveau WCAG et taxonomie de gravité par impact#1526 (ticket sur GitHub)
- AjoutéPrévuExtraction personnalisée multi-moteurs avec statut de validation#1513 (ticket sur GitHub)
- AjoutéPrévuExport du tableau d'audit conforme au mode de comparaison affiché#1509 (ticket sur GitHub)
- AjoutéPrévuVolet de recommandations par constat avec copie et liens externes#1507 (ticket sur GitHub)
- AjoutéPrévuDelta des constats entre deux scans avec coloration des améliorations#1506 (ticket sur GitHub)
- AjoutéPrévuAfficher la règle robots.txt correspondante pour chaque URL bloquée#1456 (ticket sur GitHub)
- AjoutéPrévuExtraire les annotations hreflang des en-têtes HTTP Link et des sitemaps XML#1455 (ticket sur GitHub)
- AjoutéPrévuPourcentage de similarité des paires de quasi-doublons et différences surlignées#1454 (ticket sur GitHub)
- AjoutéPrévuAudit de l'inventaire des cookies avec synthèse première partie / tierce partie#1453 (ticket sur GitHub)
- AjoutéPrévuCollecter les images d'arrière-plan CSS comme éléments de preuve d'images#1451 (ticket sur GitHub)
AjoutéPrévuConstats pour les chaînes de redirections qui se terminent par une erreur client ou#1450
serveur
#1450 (ticket sur GitHub)- AjoutéPrévuPopulation d'analyse configurable pour l'évaluation des doublons et des problèmes#1449 (ticket sur GitHub)
- AjoutéPrévuExtraire et auditer les propriétés et les liens des documents PDF#1448 (ticket sur GitHub)
AjoutéPrévuLiens internes et texte du corps présents uniquement après rendu comme constats à part#1429
entière
#1429 (ticket sur GitHub)AjoutéPrévuAjouter des contrôles pour les fichiers JS et CSS externes cassés ou interdits par#1427
robots.txt
#1427 (ticket sur GitHub)- AjoutéPrévuAjouter un contrôle pour un nombre excessif de fichiers JS et CSS par page#1426 (ticket sur GitHub)
- AjoutéPrévuAjouter un contrôle de discordance entre la langue hreflang et le lang du html#1425 (ticket sur GitHub)
- AjoutéPrévuAjouter aux scans les constats sur les variantes www et http de la page d'accueil#1423 (ticket sur GitHub)
- AjoutéPrévuAjouter aux scans les constats sur la présence et le format de llms.txt#1422 (ticket sur GitHub)
AjoutéPrévuAjouter une sonde TLS : expiration du certificat, nom d'hôte non concordant, version du#1418
protocole, chiffrements faibles
#1418 (ticket sur GitHub)- AjoutéPrévuAjouter un contrôle des ressources non HTML liées comme des liens de page ordinaires#1417 (ticket sur GitHub)
- AjoutéPrévuAjouter un contrôle des pages ayant un seul lien interne entrant#1416 (ticket sur GitHub)
- AjoutéPrévuConserver les liens externes avec rel et ajouter EXTERNAL_NOFOLLOW#1415 (ticket sur GitHub)
CorrigéPrévuLes en-têtes de sécurité et HSTS ne sont jugés que sur la réponse finale ; les étapes de#1414
redirection et l'hôte www ne sont jamais vérifiés
#1414 (ticket sur GitHub)CorrigéPrévuLes règles de rich result SoftwareApplication sont trop laxistes ; vérifier que les#1413
erreurs de données structurées de la page d'accueil remontent
#1413 (ticket sur GitHub)CorrigéPrévuLOW_TEXT_RATIO se déclenche sur presque toutes les pages ; recalibrer la mesure et le#1412
CorrigéPrévuLes constats groupés affichent un nombre de 1 et sont absents des recherches de constats#1411
par URL
#1411 (ticket sur GitHub)AjoutéPrévusite-audit émet des constats au niveau de la page pour une réponse en erreur au lieu de#1365
marquer la page comme non auditée
#1365 (ticket sur GitHub)
Projets et workflows
Workflows du propriétaire : tâches, exports, rapports planifiés et leur envoi (épique #1314)
Changement de nom
Renommage seohead → spiderhead : CLI, paquet, environnement, documentation (#1523)
- AjoutéPrévuRenommer le binaire CLI, le package et le préfixe d'environnement de seohead en spiderhead#1523 (ticket sur GitHub)
Interfaces CLI et MCP
Messages d’erreur clairs, vraie aide, sorties de lecture bornées, transports MCP (épiques #1410 #1421)
- AjoutéPrévuServeur MCP comme mode CLI de premier rang avec transports stdio et HTTP streamable#1525 (ticket sur GitHub)
- CorrigéPrévuAfficher une vraie sous-aide pour « seohead sf » au lieu d'un simple relais d'arguments#1406 (ticket sur GitHub)
Couche IA
Cibles de prompt par élément SEO au-dessus de seo_ai_column, indépendantes du fournisseur (#1524)
AjoutéPrévuAnalyse de pages assistée par IA avec cibles de prompt configurables sur les données de#1524
Qualité du code
Découpage en modules des fichiers géants et sens des dépendances (#1514 #1515)
- AjoutéPrévuSens des dépendances : supprimer les inversions d'import du cœur vers les fonctionnalités#1515 (ticket sur GitHub)
- AjoutéPrévuDécouper les modules d'adaptateur et de moteur trop volumineux en unités cohérentes#1514 (ticket sur GitHub)
Distribution: installers & one-command install
Sales-readiness theme: native installers per OS, our own package name, and a one-command CLI install. Being ready to ship is the point of this milestone.
- AjoutéPrévuOwn command name with a one-command install (pipx/brew) for the CLI#1557 (ticket sur GitHub)
- AjoutéPrévuShip native installers: DMG (macOS), Linux (AppImage/deb), Windows installer#1556 (ticket sur GitHub)
3.1.0
Première version étiquetée après le renommage de SEOHEAD Tools11 octobre 2026455 entrées · Première version étiquetée après le renommage de SEOHEAD Tools138 ajouté275 modifié42 corrigé
11 octobre 2026
5 ajouté54 modifié2 corrigéModifiéFonctionneReconstruction des paramètres de projet Planification et Crawl avec des composants natifs#1323
partagés (canvas), polices Roboto et Material Symbols hors ligne, et mise en page en colonne unique à 800 px.
#1323 (ticket sur GitHub)ModifiéFonctionneL'interface de l'application reste en anglais, les commandes de planification et les#1323
contrats de documents existants sont conservés, et seules les données de planification adossées au noyau sont affichées.
#1323 (ticket sur GitHub)ModifiéFonctionnePréserver les identités et alias des espaces de travail pendant que les paramètres du#1323
projet sont ouverts ; empêcher les enregistrements en double pendant que le noyau répond.
#1323 (ticket sur GitHub)ModifiéFonctionneLes paramètres de projet du bureau reçoivent un onglet Planification (jours de semaine,#1322
jours du mois, fenêtre d'exécution, fuseau horaire, concurrents) et un onglet Crawl (mode de crawl, préréglage User-Agent ou chaîne personnalisée, pause entre requêtes, budget d'URL, SpiderHeadBot ou un user agent personnalisé). Les deux lisent et écrivent le projet via le noyau, et l'aperçu du projet affiche la prochaine et la dernière exécution planifiée. Un mode --tray exécute les scans dus du noyau et ne maintient le Mac éveillé que pendant qu'un scan tourne.
#1322 (ticket sur GitHub)ModifiéFonctionneScans de projet planifiés (#1322, core) :
schedule.jsondans le dossier du projet#1322contient la planification (jours de la semaine et/ou jours du mois, plage horaire dans le fuseau du site, activation ou désactivation, concurrents, mode de crawl
#1322 (ticket sur GitHub)spider/sitemap/list, budgets d'URL et de requêtes par exécution, délai de politesse, et mode par tranches qui reprend le crawl partiel précédent). Il se modifie avecschedule-set/seo_schedule_seten passantexpected_revision, etschedule-showaffiche la dernière exécution et la prochaine fenêtre due.schedule-run-dueest un point d'entrée sans interface qui ne lance que les scans dus, refuse les chevauchements grâce à un bail et consigne chaque décision dans le journal du projet.schedule-installetschedule-uninstalln'écrivent ou ne suppriment un plist launchd, une ligne cron ou une commande du Planificateur de tâches que lorsqu'on les appelle explicitement.ModifiéFonctionneUser-agents de crawl (#1322) : préréglages (
spiderhead,googlebot,#1322
#1322 (ticket sur GitHub)googlebot-desktop,googlebot-smartphone,bingbot,yandexbot,yandexmobilebot,baiduspider,duckduckbot,applebot,gptbot,claudebot,perplexitybot, ainsi que des navigateurs de bureau et mobiles) ou une chaîne personnalisée. Par défaut, c'est désormaisSpiderHeadBot/<version> (+https://github.com/PavloSEO/spiderhead), et la correspondance des groupes de robots.txt utilise la même identité. Les sites qui correspondaient à l'ancien groupe robotsSEOHEAD-Toolsdoivent désignerSpiderHeadBotdans leurs règles.ModifiéFonctionneUn crawl de projet enregistré marque désormais les éléments automatiques de la checklist#1314
qu'il a mesurés.
Chaque élément de contrôle activé aboutit à l'un de trois résultats :
#1314 (ticket sur GitHub)succeededlorsque l'audit enregistré a exécuté le contrôle, avec le scan comme preuve ;unavailablelorsque le crawl l'a ignoré ou désactivé, avec la raison du crawl conservée ; ouunmeasuredlorsque l'audit ne mentionne pas du tout le contrôle. Un élément non mesuré conserve son enregistrement précédent et figure par son nom danschecklist_evidence: il ne compte donc jamais comme 0. Un crawl en échec ou interrompu marque ses contrôles automatiques commefailed, avec la raison de l'échec. Le même chemin d'enregistrement sert désormais le fluxprepare, qui disposait de sa propre copie de la boucle. Un projet dont la checklist n'est pas initialisée est ignoré, avec une raison indiquée.ModifiéFonctionneLes espaces de travail concurrents conservent l'URL telle qu'elle a été saisie : un crawl#1314
de concurrent démarre depuis son URL
#1314 (ticket sur GitHub)wwwau lieu de l'hôte nu, et un crawl d'une URL concurrente ne marque plus les éléments de checklist du propriétaire ; le résultat n'est enregistré que dans l'espace de travail du concurrent. Ouvrir un projet dont l'espace de travail concurrent est endommagé signale ce concurrent comme indisponible au lieu d'échouer.ModifiéFonctionneLes candidats concurrents peuvent être enregistrés, listés et supprimés depuis la CLI#1314
(
#1314 (ticket sur GitHub)project-competitors-add,project-competitors-list,project-competitors-remove) et via les outils MCP correspondantsseo_project_competitors_*. Chaque candidat conserve l'URL telle qu'elle a été saisie, y compris un hôtewww, et les hôtes avec ou sans www sont considérés comme le même site lors des contrôles de doublons. Un concurrent avec un hôtewwwne bloque plus la préparation et ne fait plus échouerproject-observe.ModifiéFonctionne
--max-urlsest désormais le budget de pages du crawl : un budget de pages explicite#1314complète les budgets de requêtes et de temps non définis (3 requêtes et 2 secondes par page), de sorte que les crawls de 50 à 300 pages sont admis sans
#1314 (ticket sur GitHub)approve_large_crawl. Les budgets supérieurs aux seuils du projet restent refusés.ModifiéFonctionneUn arrêt volontaire par budget d'URL est enregistré comme capture terminée avec#1314
finish_reason…Un arrêt volontaire par budget d'URL est enregistré comme capture terminée, avec
#1314 (ticket sur GitHub)finish_reasonstopped_by_budgetet une heurefinished_at, au lieu deinterruptedsans heure de fin. Ses URL en file d'attente restent disponibles pour une reprise.ModifiéFonctionneLes URL des concurrents conservent l'hôte tel qu'il a été saisi (par exemple
www.#1314reste), au lieu d'être stockées sans lui.
#1314 (ticket sur GitHub)ModifiéFonctionneL'application de bureau ajoute des sites concurrents depuis le presse-papiers ou un par#1314
un via le core, définit un plafond d'URL par site et lance leurs scans en parallèle. Un concurrent n'apparaît comme scanné qu'après un scan conservé et terminé.
#1314 (ticket sur GitHub)ModifiéFonctionneLes scans natifs mesurent HSTS, le contenu mixte et les données structurées à partir de#1314
leurs propres preuves conservées, de sorte qu'un export SF absent ou périmé ne les signale plus comme manquants ;
#1314 (ticket sur GitHub)duplicate-checkmarque une exécution sans aucune paire comparée comme non mesurée ; les constats de gabarit à l'échelle du site (même titre de gabarit, faible ratio de texte) forment un seul constat avec un nombre de pages (#1314).ModifiéFonctionneAccepter un objectif du propriétaire n'ajoute que la tâche de checklist de cet objectif.#1314
Il ne réconcilie plus tout le catalogue intégré dans
#1314 (ticket sur GitHub)coverage.json; les lignes intégrées sont ajoutées à la demande, lorsqu'on les touche.ModifiéFonctionneUne checklist occupée nomme le détenteur actif du verrou (par exemple un crawl natif en#1314
cours et son pid) au lieu de parler d'un verrou résiduel.
Le verrou enregistre le pid de son écrivain ; un verrou sans détenteur actif reste signalé comme résiduel.
#1314 (ticket sur GitHub)ModifiéFonctionneUne exécution de workflow bloquée par une étape en échec, indisponible, ignorée ou#1314
interrompue reste reprenable, et une étape en attente ultérieure peut être enregistrée au-delà d'une étape qui n'a pas réussi. Les étapes doivent toujours être enregistrées dans l'ordre d'enregistrement, et une exécution interrompue refuse toujours les étapes suivantes.
#1314 (ticket sur GitHub)ModifiéFonctionneUn examen enregistré par un agent ne rend plus un élément manuel ou livrable complet côté#1314
propriétaire.
L'élément reste en attente de la validation du propriétaire ou d'un relecteur qui ne soit pas un agent.
#1314 (ticket sur GitHub)ModifiéFonctionneLes références de l'inbox acceptent désormais les identifiants d'objectif#1314
(
#1314 (ticket sur GitHub)inbox:<uuid>), afin qu'une note puisse citer l'objectif qu'elle concerne.ModifiéFonctionneLes éléments de checklist jamais tentés et manuels affichent l'état
not_runau lieu de#1314
#1314 (ticket sur GitHub)review; seule une tentative en attente de décision reste enreview.ModifiéFonctionneLe modèle de lecture Issues (preuves de constats
observe) indique les décomptes par#1314contrôle (
#1314 (ticket sur GitHub)by_check) et unnext_offsetaprès les 20 premiers constats ;findings_pagepermet de parcourir les suivants.ModifiéFonctionneContrôles de crawl natifs (#1314) : la taille des images (
IMG_OVER_KB) est désormais#1314mesurée à partir des téléchargements d'images du crawl lui-même…
Contrôles de crawl natifs (#1314) : la taille des images (
#1314 (ticket sur GitHub)IMG_OVER_KB) est désormais mesurée à partir des téléchargements d'images propres au crawl, et le statut du sitemap (SITEMAP_URL_4XX_5XX,SITEMAP_URL_3XX,SITEMAP_URL_NON_INDEXABLE) à partir de la récupération, par le crawl, de chaque URL listée dans le sitemap. Une URL listée que le crawl n'a jamais demandée est comptée comme non mesurée, jamais comme saine.IMG_MISSING_ALT,IMG_MISSING_DIMENSIONSetHREFLANG_ERRORindiquent désormais, sur un scan natif, que le crawl ne les mesure pas selon la définition de l'export SF, au lieu de renvoyer vers un filtre SF. Un hreflang manquant ou un contenu mixte sur un scan natif est signalé comme non mesuré par le crawl natif, et non comme un export manquant.ModifiéFonctionneJournal de projet (#1314) : chaque événement du projet est désormais aussi écrit sous#1314
forme d'une puce dans
#1314 (ticket sur GitHub)log.md, et le core enregistre des entrées automatiques pour les crawls terminés (scans), les rapports de projet générés et les tentatives de checklist (agent). Les entrées vont dansevents.jsonlet sont lues parproject-event-page; les déclencheurs automatiques ne font jamais échouer le crawl, le rapport ou l'étape de checklist qu'ils décrivent.project-event-appendetseo_project_event_appendconservent leur interface et ajoutent la ligne danslog.md.ModifiéFonctionneLe crawl natif stocke désormais les en-têtes de sécurité lus par les contrôles#1314
(
#1314 (ticket sur GitHub)Strict-Transport-Security,Content-Security-Policy,X-Frame-Options,X-Content-Type-Options,Referrer-Policy,Permissions-Policy). Auparavant, ils étaient écartés à la capture : un site qui envoyait HSTS était signalé comme ne l'ayant pas. Les en-têtes Set-Cookie ne conservent que leurs attributs (secure,httponly,samesite=), jamais le nom ni la valeur du cookie. Un en-tête de sécurité envoyé plusieurs fois sur la même page est signalé commeDUPLICATE_SECURITY_HEADER.ModifiéFonctionne
workflow-startn'exige qu'un objectif accepté par le propriétaire :--goal-id(ou#1314
#1314 (ticket sur GitHub)context.goal_id) suffit. Le prompt enregistré estskill:workflow/full-audit-v1par défaut, et la tâche de checklist est, par défaut, celle qui accompagne l'objectif accepté (custom:goal-<entry>). Les refus indiquent la commande propriétaire qui corrige le lien manquant. Les options sont documentées dansseohead workflow-start --help.ModifiéFonctionneLes populations de checklist acceptent des objets partiels : seuls
kindetsource#1314sont obligatoires, et les autres clés sont par défaut non définies. Une population refusée indique ses clés manquantes ou inconnues.
#1314 (ticket sur GitHub)ModifiéFonctionneLes crawls natifs de projet se rabattent sur
/sitemap.xmlde l'origine lorsque#1314robots.txt ne déclare aucun sitemap. Un repli absent est consigné comme incomplet, mais n'interrompt pas le crawl.
#1314 (ticket sur GitHub)ModifiéFonctionneLes journaux serveur conservent désormais les totaux exacts d'IP de bots sur disque au#1311
lieu d'un échantillon plafonné, avec…
Les journaux serveur conservent désormais les totaux exacts d'IP de bots stockés sur disque au lieu d'un échantillon plafonné, avec des enregistrements par IP bornés, des visites, la dernière visite et des filtres par sous-réseau. La vérification DNS explicite contrôle séparément chaque IP sélectionnée, enregistre les preuves PTR et de résolution directe, prend en charge l'IPv6, et conserve les résultats indisponibles ou hors délai comme non vérifiés. Les caches locaux expirent au bout d'une heure ; les lectures ne déclenchent jamais de DNS.
#1311 (ticket sur GitHub)AjoutéFonctionneAjout de l'import local des journaux de projet, du résumé paginé, des preuves de bots et#1311
des jointures avec les scans natifs, via les gestionnaires CLI/MCP partagés. Les artefacts de journaux séparés conservent les partitions par date ainsi que la couverture et la provenance des sources, sans modifier les schémas figés des scans. Des preuves manquantes ou incomplètes retiennent toute conclusion d'absence. Les écrans de journaux de l'application de bureau restent un chantier distinct.
#1311 (ticket sur GitHub)ModifiéFonctionneContrôle des appels de production Google DataForSEO avant la construction du client,#1310
devis hors ligne à partir des prix fournis, prise en compte de la profondeur SERP et des frais des opérateurs de recherche, et application des plafonds USD connus par appel avant tout transport. Élargissement des opérations bornées sur l'index des backlinks ; les coûts manquants et les formes de réponse mal formées sont consignés dans le journal comme reçus incertains. Les plafonds en devise par projet et par mois restent en attente d'une décision du propriétaire.
#1310 (ticket sur GitHub)ModifiéFonctionneFusion des preuves explicites de source et d'import de backlinks dans les instantanés#1310
d'historique locaux privés, avec les observations complètes des jetons rel, les ancres, les domaines référents, les filtres et des signaux transparents de revue de spam. Ajout de commandes CLI et d'outils MCP pour le profil et l'export de désaveu relu, sans aucune soumission automatique.
#1310 (ticket sur GitHub)AjoutéFonctionneAjout de notices de revue pour les jetons rel mixtes et les mentions sponsorisées#1310
sortantes, à partir des preuves natives conservées. Le contexte indisponible est préservé, les décomptes nuls servent aux populations HTML entièrement non analysées, et les classes sponsored, ugc et nofollow apparaissent dans les rapports et les contrôles des sites donneurs, sans modifier le schéma figé des scans conservés.
#1310 (ticket sur GitHub)ModifiéFonctionneLes contrôles DOM et de doublons natifs lisent désormais le document conservé de chaque#1307
page par URL exacte, avec une traçabilité vérifiée de la réponse et du corps. Une conservation désactivée, des corps manquants, invalides ou au-delà du budget restent des omissions explicites ; les constats partiels portent leur couverture. Les empreintes exactes du contenu principal et les constats SimHash/LSH bornés sont persistés dans l'audit natif, sans répertoire HTML SF. Les bandes LSH adaptées au seuil préservent le rappel des paires qualifiantes tout en réduisant les compartiments candidats trop larges ; l'épuisement du budget d'analyse approchée ne supprime pas les constats par empreinte exacte.
#1307 (ticket sur GitHub)ModifiéFonctionneLes audits SF peuvent exécuter une similarité sémantique et une revue titre/H1 face au#1307
corps conservé…
Les audits SF peuvent exécuter, en option, une similarité sémantique et une revue titre/H1 par rapport au corps conservé, via un adaptateur local fourni explicitement ou des embeddings déclarés. Aucun modèle ni fournisseur n'est choisi automatiquement. L'identité de l'adaptateur, les empreintes des sources, les scores, les limites et les omissions accompagnent les candidats à la revue ; la similarité sémantique n'affirme ni contenu dupliqué, ni pertinence par rapport à la requête, ni cannibalisation. Les deux lignes de couverture sémantique SF restent des lacunes tant qu'aucun adaptateur réel n'est choisi et validé. L'autorisation d'un fournisseur externe exige le booléen littéral
#1307 (ticket sur GitHub)true; des chaînes ou des entiers évalués comme vrais n'autorisent jamais un appel d'embedding.ModifiéFonctionneLes audits natifs conservés regroupent désormais les liens internes cassés par cible#1306
observée, avec les preuves source et ancre, enregistrent les chaînes et boucles de redirections avec le statut de chaque saut, distinguent la recherche interne basée sur le chemin et les ancres vides, et exposent les résultats d'absence de réponse externes capturés. Les constats de cohérence du sitemap et de robots.txt utilisent l'appartenance et les preuves de réponse conservées, sans nouvelle requête en direct ; les preuves manquantes ou partielles restent explicites.
#1306 (ticket sur GitHub)AjoutéFonctionneAjout d'un graphe de liens borné pour les scans conservés, avec des filtres par position,#1306
section et voisinage local, une agrégation par section et les changements d'arêtes d'un scan à l'autre. La réanalyse hors ligne complète les positions par lots dérivés reprenables, et signale les preuves non classées dans l'inspection des liens et l'état du scan, tout en préservant le schéma figé scan.v1.
#1306 (ticket sur GitHub)AjoutéFonctionneAjout de la génération locale de sitemap XML à partir des pages 200 conservées,#1306
canoniques et indexables, avec la lastmod observée, des exclusions, la découpe par protocole, un index de sitemaps et un sitemap d'images facultatif. La CLI et le MCP partagent les mêmes implémentations ; la publication des fichiers exige un dossier local vide et neuf, et ne déploie jamais rien.
#1306 (ticket sur GitHub)ModifiéFonctionneConserve les cibles de découverte manquantes relevées au moment de la capture lorsque le#1306
traitement ultérieur du sitemap ou des seeds ajoute l'URL au catalogue conservé. Les preuves non résolues ou exclues, déjà expliquées, restent immuables, tandis que les cibles connues revendiquées et les incohérences d'identité non nulles restent invalides.
#1306 (ticket sur GitHub)ModifiéFonctionneLa génération du sitemap d'images exclut les réponses de ressources non-200 connues, même#1306
lorsque la ressource n'a pas été stockée comme page crawlée, et marque les réponses d'images non mesurées comme couverture partielle.
#1306 (ticket sur GitHub)ModifiéFonctionneConserve les attributs canonical et de pagination bruts comme contexte de document#1305
versionné, sans modifier le SQL figé du scan. Les liens canoniques portant des attributs hreflang, lang, media ou type non pris en charge, des annotations mal formées ou des déclarations inter-hôtes et inter-protocoles produisent désormais des constats liés à leurs preuves ; les mots-clés de relation bien formés mais inconnus restent non classés.
#1305 (ticket sur GitHub)ModifiéFonctionneÉtend les sondes bornées de cible au même hôte à la pagination et conserve les#1305
observations mesurées de noindex et de canonical. Les chaînes canoniques natives et les constats noindex et canonical/hreflang peuvent utiliser les sondes ; les anciennes sondes ne portant que le statut restent lisibles et non mesurées pour les directives HTML.
#1305 (ticket sur GitHub)ModifiéFonctionneJuge les chaînes de pagination natives opaques pour détecter les contradictions#1305
réciproques explicites next/prev, en conservant les règles numérotées ou à pas et les états non jugés nommés. Sélectionne les URL des constats conservés, y compris les membres groupés, via scan-url-query, à l'aide de tables SQLite temporaires et d'un flux de preuves audit.v2.
#1305 (ticket sur GitHub)CorrigéFonctionneGarde non classés les jetons de relation Unicode inconnus et ceux contenant de la#1305
ponctuation, normalise les autorités canoniques IDN et port par défaut équivalentes, et préserve les chemins exacts des cibles de pagination natives. Les jetons de page mixtes illisibles restent non jugés ; le défaut de pagination de la boutique est détecté même lorsque la page un ne contient aucun numéro dans son URL.
#1305 (ticket sur GitHub)ModifiéFonctionneRend les annotations next/prev déclarées de la fixture boutique, afin que sa vérité de#1305
référence de pagination D41 soit réellement servie et validable par la capture native.
Rend les annotations next/prev déclarées de la fixture boutique, afin que sa vérité de référence de pagination D41 soit réellement servie et puisse être validée par la capture native.
#1305 (ticket sur GitHub)ModifiéFonctionneConserve les preuves AMP statiques et d'accessibilité liées au document dans les lignes#1304
de contexte de scan existantes, en préservant le SQL figé
#1304 (ticket sur GitHub)scan.v1. Les constats AMP structurels examinent la déclaration doctype d'origine, le head et le body, le charset UTF-8, le viewport, le script d'exécution et les déclarations de boilerplate ; les cibles AMP appariées sans marqueur de racine AMP sont identifiées séparément. Les règles complètes du validateur AMP restent indisponibles dans l'attente d'une décision de licence.ModifiéFonctionneLance les constats d'accessibilité sur la langue du document, les libellés de formulaire,#1304
les alternatives d'images, les sauts de titres et le repère main. Les constats conservent des exemples bornés et les décomptes complets des occurrences. Le contraste calculé et les règles axe-core rendues restent non mesurés.
#1304 (ticket sur GitHub)ModifiéFonctionneValide les microdonnées Schema.org et les sujets et propriétés RDFa courants avec le#1304
vocabulaire embarqué et les règles existantes de résultats enrichis. Les erreurs, avertissements et propriétés obligatoires manquantes conservés par URL deviennent des constats natifs. Les constructions RDFa non prises en charge constituent une preuve partielle explicite, et non un résultat de validation propre.
#1304 (ticket sur GitHub)ModifiéFonctionneSert les arbres JSON-LD analysés avec les erreurs de source, de position et de syntaxe de#1304
chaque bloc dans…
Sert les arbres JSON-LD analysés, avec pour chaque bloc les erreurs de source, de position et de syntaxe, dans
#1304 (ticket sur GitHub)scan-structured-blocks; plafonne les arbres de propriétés larges et expose leur limite. Lit le dernier document pour une URL et une représentation demandées sans modifier son scan.ModifiéFonctionneConserve les preuves structurées partielles ou indisponibles sans masquer les erreurs#1304
confirmées des blocs voisins.
Résout les identités JSON-LD prises en charge par rapport à la même base de document que le balisage HTML, tout en préservant le JSON source ; rejette les blocs JSON scalaires ou vides comme graphes valides, et partage la sélection normalisée des balises JSON-LD en direct entre l'analyseur et les projections servies.
#1304 (ticket sur GitHub)ModifiéFonctionneLes audits natifs exposent désormais le balisage bloquant le rendu dans le head à partir#1303
du HTML conservé complet…
Les audits natifs exposent désormais le balisage bloquant le rendu dans le head, à partir du HTML conservé complet, sans modifier le schéma figé scan.v1. Les corps manquants, incomplets ou limités par le budget restent indisponibles. La détection existante des ressources du head et l'entrée d'extraction personnalisée SF sont réutilisées.
#1303 (ticket sur GitHub)AjoutéFonctionneAjoute des budgets configurables d'octets pour la navigation observée native et pour#1303
chaque ressource individuelle, des signaux de livraison du cache et des polices, des heuristiques de minification et de scripts hérités, ainsi que les doublons exacts de scripts sur une même page, à partir des preuves scan.v2 conservées. Les données non téléchargées et les charges utiles d'exécution incomplètes restent explicites ; les déclarations responsives ne sont pas additionnées comme poids d'exécution. Les bornes inférieures HTML seules, les décomptes de couverture scalaires, les échantillons de polices et les résumés de sources restent explicites et bornés.
#1303 (ticket sur GitHub)ModifiéFonctionneCollecte une couverture JS/CSS Chromium précise et bornée lors des rendus demandés,#1303
conserve les résumés d'usage en UTF-16 ainsi que les constats de dimensionnement d'images rendues, et les relit après réouverture ou réanalyse hors ligne. Les moteurs non pris en charge et les mesures manquantes ne deviennent jamais des résultats propres.
#1303 (ticket sur GitHub)ModifiéFonctionneAccepte le JSON Lighthouse par URL correspondant et borné pour la découverte du LCP, les#1303
arbres de dépendances, les responsables des décalages de mise en page, la livraison des images, les reflows forcés et les constats d'exécution des scripts, avec la provenance de capture et les empreintes des entrées. Aucun processus Lighthouse, fournisseur payant ni inférence des CWV terrain n'est ajouté. Actualise le registre, la couverture SF et la documentation de référence générée.
#1303 (ticket sur GitHub)CorrigéFonctionneGarde les seuils additifs d'actifs hors de l'en-tête figé du scan natif et stocke leurs#1303
valeurs exactes dans un contexte d'exécution validé et versionné. La reprise compare ces valeurs séparément, et la réanalyse hors ligne les restaure à partir des preuves conservées ; les anciens lecteurs légers peuvent toujours inspecter la forme inchangée de la configuration d'en-tête.
#1303 (ticket sur GitHub)ModifiéFonctionne
recordde la checklist accepte désormais un contrôle automatique réussi dont le scanconservé est un scan audit.v2 en flux ; auparavant, il échouait avec
'NoneType' object is not subscriptable, car l'audit se trouve dans le fichier compagnon et non dans la table héritée du scan. Les formats d'enregistrement acceptés sont documentés dansseohead project checklist-record --help, dans le schéma de l'outil MCPseo_project_checklist_recordet dansdocs/INPUTS.md.ModifiéFonctionneExécute la suite complète du toolkit une seule fois sur Python 3.15 au lieu d'une matrice
de versions plus anciennes, en conservant quatre groupes de tests vérifiés et la couverture combinée. La même version stable de Python sert à la CI et aux amorçages des builds de release.
ModifiéFonctionneLa liste Work de l'application de bureau lit chaque page de checklist (100 par requête)
au lieu des 50 premières lignes ; Issues nomme chaque contrôle principal du registre au lieu d'afficher « Autre contrôle ».
ModifiéFonctionneLa vue Work de l'application de bureau lit toutes les pages de checklist, mais n'affiche
le panneau des tâches du projet qu'une page à la fois, de sorte qu'une checklist de n'importe quelle taille n'échoue plus avec « at most 100 rows ». L'application ouvre le scan que laisse derrière elle un audit complet (le plus grand scan complet) lorsque le propriétaire n'en a pas choisi un, et la découverte du core vérifie aussi l'environnement propre de l'interpréteur de l'application (
sys.prefix) avant le PATH.ModifiéFonctionneConsolide les contrôles d'audit conservés et les améliorations natives de l'application
de bureau.
Préserve les réglages de budget de ressources sans modifier l'en-tête figé du scan, conserve une réanalyse hors ligne et une reprise sûres, et partage les composants natifs des réglages de projet, les tokens de thème et les polices embarquées. Retire les entrées de design privées de l'arborescence distribuée.
ModifiéFonctionneLie les revérifications de remédiation à la politique d'actifs effective validée tout en
préservant l'en-tête de scan d'origine…
Lie les revérifications de remédiation à la politique d'actifs effective validée, tout en préservant l'en-tête de scan d'origine et les octets source.
ModifiéFonctionneLa couverture de checklist de projet lie désormais les rapports audit.v2 en flux :
report-build --projectetaudit-workflow --action reportjoignent l'instantané de checklist aux sorties xlsx, docx, csv, md et PDF, au lieu de produire un rapport non lié. Un scan arrêté par son budget d'URL (stopped_by_budget) est accepté comme dernier scan audité du projet, et son rapport indique la portée limitée.report-build --inputaccepte la cléformat.report-build --project --format jsonsur un audit en flux est refusé avec une explication, car le JSON est écrit tel quel et ne peut pas porter l'instantané de checklist.
10 octobre 2026
16 ajouté51 modifiéModifiéFonctionneLe moniteur de scan de l'application de bureau suit le tableau de conception ScanRun :#1203
pourcentage réel à partir des compteurs fetched/found…
Le moniteur de scan de l'application de bureau suit le tableau de conception ScanRun : pourcentage réel calculé à partir des compteurs fetched/found, barre de progression de 10 px, limite de débit du collecteur, et un emplacement réservé de 120 px, sans fausse donnée, pour le graphique de vitesse (l'historique des débits du core est suivi dans #1203).
#1203 (ticket sur GitHub)ModifiéFonctionne
check_sampledansseohead/checks/custom_extract.pyexécute les extracteurs CSS, XPath#1059et regex sur un seul document HTML enregistré, sans scan ni requête réseau. Chaque résultat porte ses valeurs et une
#1059 (ticket sur GitHub)reasonpour un résultat vide (no_match,empty_htmloubudget_exceeded), afin qu'une réponse vide ne soit jamais présentée comme un résultat propre (#1059). Il s'agit uniquement d'un appel du core ; aucun outil CLI ni MCP n'est encore enregistré, et la récupération d'une URL en direct est volontairement exclue.ModifiéFonctionneLe contrôle de séquence de pagination (
PAGINATION_SEQUENCE_ERROR) juge désormais les#1057séries à pas : lorsqu'un pas couvre la plupart des pas d'une série, une rupture de ce pas est signalée (
#1057 (ticket sur GitHub)?page=1,3,5,8rompt entre 5 et 8). Les schémas de décalage irréguliers et les séries sans numéro restent non jugés et sont nommés comme ignorés.AjoutéFonctionneNouveau contrôle de crawl
INTERNAL_NOFOLLOW_OUTLINKS(notice) : indique chaque page#1052source qui…
Nouveau contrôle de crawl
#1052 (ticket sur GitHub)INTERNAL_NOFOLLOW_OUTLINKS(notice) : il signale chaque page source qui porte des liens sortants internes ennofollow, avec leur nombre et un échantillon borné de destinations. Il comble l'écart au niveau de la page pour la ligne « Internal Nofollow Outlinks » de Screaming Frog, restéepartial. Il s'exécute sur les crawls natifs et est ignoré par son nom lors des exécutions basées sur un export seul ou sur une liste d'URL.ModifiéFonctionneLe parseur lit les
<link rel="alternate">qualifiés par média, sanshreflang…#1051Le parseur lit les déclarations
#1051 (ticket sur GitHub)<link rel="alternate">qualifiées par média, sanshreflang(une URL mobile distincte), et les range dansparse_htmlsousmobile_alternates(#1051). Le contrôle SFMobile Alternate Linkreste un écart : le contrôle qui compare la balise canonical de l'URL mobile n'est pas encore écrit.AjoutéFonctionneAjout du contrôle
HREFLANG_UNLINKED_TARGET(warning) : une cible d'alternate hreflang#1050qu'aucun…
Ajout du contrôle
#1050 (ticket sur GitHub)HREFLANG_UNLINKED_TARGET(warning) : toute cible d'alternate hreflang qu'aucun lien hypertexte ordinaire du crawl ne pointe est signalée. La ligne « Unlinked Hreflang URLs » de Screaming Frog est donc désormais couverte. Le contrôle lit l'export All Hreflang et la colonne Internal:All Inlinks, comme le faitUNLINKED_CANONICAL; une auto-référence n'est pas comptée, et un crawl partiel retient le constat. Le registre passe de 182 à 183 contrôles.ModifiéFonctionneDimensionnement des images sur les routes rendues : render-check enregistre désormais la#1049
taille…
Dimensionnement des images sur les routes rendues : render-check enregistre désormais, pour chaque image, sa taille intrinsèque en pixels par rapport à sa boîte rendue et au device pixel ratio (
#1049 (ticket sur GitHub)image_sizing: preuves de surdimensionnement et d'agrandissement). Il ne s'agit que de preuves ; aucun identifiant de contrôle SF-analyzer n'existe encore, donc « Incorrectly Sized Images » passe de gap à partial (#1049).ModifiéFonctionneSemantically Similar : le statut reste
gap, et la note indique désormais le blocage.#1041La commande et l'outil MCP de similarité sémantique existent, mais aucun adaptateur d'embeddings n'est livré et l'exécution SF n'appelle pas encore le module (#1041).
#1041 (ticket sur GitHub)AjoutéFonctionneAjout du contrôle
INTERNAL_LINK_SPONSORED_UGC(notice) : un lien du même site portant#1040
#1040 (ticket sur GitHub)rel="sponsored"ourel="ugc"est signalé avec l'ensemble complet de ses jetons rel. Il nécessitelink_attributes.capture, comme les autres contrôles fondés sur rel, et est signalé comme non mesuré dans le cas contraire.AjoutéFonctionneAjout du cœur de génération de sitemap XML à partir d'un scan conservé :#1039
select_indexablene garde…Ajout du cœur de génération de sitemap XML à partir d'un scan conservé.
#1039 (ticket sur GitHub)select_indexablene retient que les pages HTML en 200 qui sont auto-canoniques, nonnoindexet non bloquées par robots.txt, et indique la raison de chaque URL exclue.render_sitemapsécrit un<urlset>, ou des fichiers découpés plus un<sitemapindex>dès 50 000 URL ou 52 428 800 octets par fichier. L'exposition en CLI et en MCP suivra.AjoutéFonctionneAjout de
cloudflare-traffic(CLI) etseo_cloudflare_traffic(MCP) : une source#1026Cloudflare GraphQL Analytics en lecture seule…
Ajout de
#1026 (ticket sur GitHub)cloudflare-traffic(CLI) etseo_cloudflare_traffic(MCP) : une source GraphQL Analytics de Cloudflare en lecture seule, pour l'analyseur de logs lorsque les logs d'origine sont absents (#1026, #1009). Elle renvoie bots × URL × codes de statut × statut de cache par jour, au format delog-analyze, avec le libellésource: cloudflare-aggregated, car il s'agit de données de bord agrégées et non de logs bruts (pas d'IP client, donc pas de vérification par reverse DNS). Les jours qui atteignent le plafond de 10 000 lignes sont découpés en fenêtres horaires ; l'appel est compté dans le journal des dépenses comme fournisseur gratuit. Le jeton provient deCLOUDFLARE_API_TOKENou deSEOHEAD_CLOUDFLARE_TOKEN_FILEet n'est jamais affiché.ModifiéFonctionneÉvaluateur interne des constats Search Console, pas encore relié aux rapports :#1025
#1025 (ticket sur GitHub)seohead/checks/search_console_findings.pyjoint les pages du crawl à un document de preuves GSC normalisé et évalue GSC_ORPHAN, NO_SEARCH_DATA et NON_INDEXABLE_WITH_SEARCH_DATA. Un contrôle est ignoré, avec sa raison, lorsque le crawl est partiel, que la fenêtre est incomplète, que la collecte est échantillonnée ou tronquée, ou qu'aucune preuve n'est fournie ; un contrôle ignoré n'est jamais présenté comme propre.ModifiéFonctionne
crawl-enrichsignale quatre constats d'analytics web à partir d'un CSV hors ligne#1024indexé par URL : URL orphelines selon les analytics, pages indexables sans donnée d'analytics, pages non indexables avec des visites, et taux de rebond supérieur à 70 %. Les options
#1024 (ticket sur GitHub)--visits-columnet--bounce-columnnomment les colonnes à lire ; une colonne absente est refusée par son nom, les valeurs illisibles sont comptées et jamais lues comme zéro, et les URL orphelines selon les analytics sont retenues sur un crawl partiel. La récupération en direct, URL par URL, depuis Metrika ou GA4 n'est pas encore incluse (#990, #984).ModifiéFonctionneLa carte de couverture des problèmes SF liste les quatre problèmes d'analytics web#1024
(Orphan URLs, Bounce Rate…
La carte de couverture des problèmes SF liste désormais les quatre problèmes d'analytics web (Orphan URLs, Bounce Rate Above 70%, No GA Data, Non-Indexable with GA Data) comme relevés par
#1024 (ticket sur GitHub)crawl-enrichà partir d'un CSV hors ligne, au lieu de les marquer hors périmètre.sf runne les émet toujours pas.ModifiéFonctionneSocle de couverture lab pour le CSS et le JavaScript inutilisés (#1023, tranche 1) : un#1023
analyseur pur prend…
Socle de couverture lab pour le CSS et le JavaScript inutilisés (#1023, tranche 1) : un analyseur pur lit les charges
#1023 (ticket sur GitHub)page.coveragede Playwright et indique, pour chaque ressource, le nombre de caractères inutilisés, avec un constat lorsqu'une ressource est en grande partie inutilisée et volumineuse. La collecte de la couverture sur une page rendue fera l'objet de la tranche suivante.ModifiéFonctionneContrôles de paire AMP (#1020) :
AMP_NON_200,AMP_MISSING_CANONICAL,#1020
#1020 (ticket sur GitHub)AMP_MISSING_RETURN_LINK,AMP_NON_INDEXABLE_CANONICALetAMP_INDEXABLElisent la cible AMP déclarée par une page desktop avec<link rel="amphtml">. Seules les pages AMP capturées par le crawl sont jugées ; une cible non capturée donne un contrôle ignoré, avec sa raison. Le crawl natif enregistre désormais la cible résolue dans la colonneAMP HTML. Les dix autres règles AMP de SF restent hors périmètre.AjoutéFonctionneAjout de
HTML_OVER_2MB, un plafond absolu de 2 Mo sur un document HTML, distinct de#1019
#1019 (ticket sur GitHub)LARGE_HTML, qui est relatif à la médiane. La ligne « HTML Document Over 2MB » de Screaming Frog pointe désormais vers ce contrôle, et non plus vers la règle des 200 Ko.AjoutéFonctionneAjout de la notice
URL_HAS_PARAMETERSà l'audit de crawl : une page HTML interne dont#1018l'URL porte des paramètres de requête est signalée, quel que soit son statut canonique.
#1018 (ticket sur GitHub)URL_HAS_PARAMSconserve son sens plus étroit, limité aux pages sans balise canonical (#1018).ModifiéFonctionneDirectives robots (#1017) : les anciennes
noodpetnoydirdans la meta robots ou#1017X-Robots-Tag…Directives robots (#1017) : les anciennes
#1017 (ticket sur GitHub)noodpetnoydir, présentes dans la meta robots ou dansX-Robots-Tag, déclenchent désormais les noticesNOODPetNOYDIRdans l'audit SF, alors qu'elles étaient listées hors périmètre.none(noindex + nofollow) est couvert par des tests de non-régression pour le cheminX-Robots-Tagcomme pour le chemin meta.AjoutéFonctionneNouveau contrôle de crawl
JS_CONSOLE_ERRORS(SF « Pages with JavaScript Errors ») : une#1015page dont la console du rendu conservé a enregistré des erreurs. Il est lu hors ligne, à partir des sidecars de console sauvegardés d'un scan natif, sans nouveau rendu ; un enregistrement illisible donne un contrôle ignoré et motivé, jamais une page déclarée propre. Nécessite
#1015 (ticket sur GitHub)rendering.artifacts.console_errors.ModifiéFonctionneLes crawls signalent désormais quatre en-têtes de sécurité manquants par page HTML,#1013
d'après la réponse finale conservée :
#1013 (ticket sur GitHub)MISSING_CSP,MISSING_X_CONTENT_TYPE_OPTIONS,MISSING_X_FRAME_OPTIONS(satisfait par une directive CSPframe-ancestors) etMISSING_REFERRER_POLICY. Les pages dont les en-têtes conservés ne peuvent pas être analysés sont comptées comme non mesurées, et non comme propres ; un crawl sans scan conservé ignore ces pages en indiquant la raison.AjoutéFonctionneAjout d'un validateur des specs d'actions de page à liste fermée (
click,scroll,#1011
#1011 (ticket sur GitHub)wait_for_selector,wait_ms), avec un budget de temps statique pour les crawls rendus. Il n'est pas encore branché au rendu : le comportement des crawls est donc inchangé (#1011).ModifiéFonctionne
log-analyzelit les journaux d'accès compressés en gzip (fichiersaccess.log.N.gz#1009pivotés et archives renommées)…
#1009 (ticket sur GitHub)log-analyzelit directement les journaux d'accès compressés en gzip (fichiersaccess.log.N.gzpivotés et archives renommées), en détectant le format d'après le contenu et non d'après l'extension. Une archive tronquée ou corrompue est signalée comme erreur, sans comptages partiels.ModifiéFonctionne
scan-url-queryajoute des préréglages de vue prêts à l'emploi (--preset, par exemple#1007
#1007 (ticket sur GitHub)status_4xx,title_missing,title_over_60,h1_missing,noindex_meta), combinés par un ET logique avec vos filtres.--export PATHavec--export-format csv|xlsxécrit chaque ligne correspondante dans un nouveau fichier. L'export est refusé au-delà de--export-max-rows(100 000 par défaut) et n'écrase jamais un fichier existant.ModifiéFonctionneLes règles d'éligibilité aux résultats enrichis quittent le code Python pour#1001
#1001 (ticket sur GitHub)seohead/data/rich_results_rules.json, un fichier de données versionné, livré avec le paquet. Sa valeurversionest reprise commerules_versionsur chaque verdict de résultat enrichi : modifier une règle revient donc à modifier des données, et non du code.ModifiéFonctionneLes verdicts résultats enrichis (
check_schemaet les contrôles de preuves structurées#1001stockées) portent désormais
#1001 (ticket sur GitHub)rules_version, afin que chaque résultat indique l'ensemble de règles hors ligne qui l'a produit.ModifiéFonctionneDocumentation de six scripts de maintenance et de génération absents de la documentation…#997
Documentation des six scripts de maintenance et de génération qui n'étaient cités nulle part dans la documentation (
#997 (ticket sur GitHub)scripts/generate_project_catalogue.py,generate_registry_inventory.py,render_readme_visuals.py,linux_lifecycle_smoke.py,capacity_watchdog.py,profile_million_audit.py) dansdocs/README.md, avec ce que chacun écrit et ceux qui tournent en CI (#997).AjoutéFonctionneAjout du pipeline semantic core sous
seohead/semantics, avec sa base SQLite propre à#995chaque projet…
Ajout du pipeline semantic core sous
#995 (ticket sur GitHub)seohead/semantics, avec sa propre base SQLite par projet :seohead semantics <stage> --project DIR(init, import, collect, clean, graph, exact, cluster, competitors, synonyms, report, excel, mine, sitematch, relevance, status, export), les outils MCPseo_semantics_status,seo_semantics_run,seo_semantics_mineetseo_semantics_paid(les étapes payantes exigentconfirm_paid=true), le clustering Louvain derrière le nouvel extrasemantics, le skillsemantic-corelivré avec le paquet et un projet d'exemple synthétique. Les frais fournisseurs d'une étape sémantique sont marqués dans le journal de dépenses partagé (#995).ModifiéFonctionneSuppression de l'adaptateur bot Telegram guidé
seohead/integrations/bot; les accusés#995de livraison du monitoring vivent désormais dans
#995 (ticket sur GitHub)seohead/projects/service_delivery.py(#995).ModifiéFonctionne
scan-url-queryrenvoieclick_depth, le plus court chemin de liens internes depuis la#992page de départ du scan, sous forme de colonne, de filtre et de clé de tri. Les pages qu'aucun lien stocké n'atteint valent
#992 (ticket sur GitHub)NULL(orphelines).crawl_depthne change pas et reste la profondeur de la frontière de crawl, qui vaut 0 pour toute URL issue du sitemap. Un scan qui n'a pas conservé ses liens échoue avecreason_code: click_depth_unavailableau lieu d'afficher un fauxNULL. La valeur est recalculée à chaque requête à partir de la tablelinks; la stocker en colonne indexée à la fin du scan reste à traiter, car cela suppose une décision sur la version du schéma (#992).ModifiéFonctionne
scan-url-queryet son outil MCP acceptent un argumentfacetsfacultatif (une liste#991d'identifiants de groupes, ou all)…
#991 (ticket sur GitHub)scan-url-queryet son outil MCP acceptent un argument facultatiffacets(une liste d'identifiants de groupes, ouall) qui renvoie, en une seule passe et sur les mêmes filtres, le nombre de résultats par groupe (status_2xx–5xx,title_missing,canonical_missing,directives_noindex/nofollow,with_outlinks). Les groupes qui exigent une projection (internes ou externes, sitemap, doublon et longueur de title, canonical pointant sur soi ou ailleurs) ne sont pas encore inclus.ModifiéFonctionneLiaisons de sources de projet, première tranche (#990) :
project-sources-link,#990
#990 (ticket sur GitHub)project-sources-unlinketproject-sources-list(CLI, ainsi queseo_project_sources_link|unlink|listvia MCP) enregistrent les identifiants de ressources fournisseur qu'un projet local utilise, danssources.jsonà côté deproject.json. Hors ligne seulement : ni découverte, ni appel fournisseur, ni identifiants ;project.jsonn'est jamais réécrit.ModifiéFonctionneCrawl natif : nouveau contrôle IMG_BROKEN (#989, tranche 1) signale les cibles de <img>#989
ayant répondu 4xx/5xx, avec la page source comme preuve. Il lit le graphe de ressources stocké et s'ignore honnêtement lorsqu'aucune image n'a été mesurée.
#989 (ticket sur GitHub)AjoutéFonctionneAjout d'un classifieur pur de soft 404 sur le corps de page stocké#988
(
#988 (ticket sur GitHub)classify_stored_soft404) : texte vide, phrase d'erreur sur une page légère, et correspondance simhash avec un gabarit 404. Il n'est pas encore branché au crawl et n'émet aucun constat ; c'est la première tranche de l'issue #988.ModifiéFonctionneLes crawls natifs sondent les cibles canonical et hreflang qu'aucun lien n'a atteintes,#987
une fois par cible…
Les crawls natifs sondent, une fois par cible après le crawl (5 000 au maximum), les cibles canonical et hreflang qu'aucun lien n'a atteintes. Une cible en 4xx/5xx ou en redirection donne alors CANONICAL_TARGET_ERROR, CANONICAL_TO_REDIRECT ou HREFLANG_BROKEN_TARGET, comme cela se faisait déjà pour les cibles crawlées (#987).
#987 (ticket sur GitHub)ModifiéFonctionneLes constats de chaîne et de boucle de redirections issus d'un crawl natif portent#985
désormais le chemin complet des sauts (
#985 (ticket sur GitHub)details.path), pour qu'une correction puisse nommer chaque saut à remplacer. Réf. #985.ModifiéFonctionneLes constats de chaîne et de boucle de redirections issus d'un crawl natif portent#985
désormais aussi…
Les constats de chaîne et de boucle de redirections issus d'un crawl natif portent aussi
#985 (ticket sur GitHub)details.inbound_links(liens internes pointant encore vers la première URL de la chaîne, avec leurs ancres, plafonnés à 20) etinbound_links_count. Sans inventaire all_inlinks, le champ vautnullavec une raison. Réf. #985.ModifiéFonctionneLes projets peuvent enregistrer un lien vers leur semantic core sous forme de faits#984
saisis par l'opérateur (
#984 (ticket sur GitHub)semcore_project,semcore_cluster_count,semcore_landing_mapping). Le semantic core reste dans sa propre base et n'est pas fusionné dans la base du projet ; la détection n'écrit jamais ces faits, et un fait fourni ne peut pas revendiquer une provenance de détection. Documenté dansdocs/PROJECTS.md, section « Linking a semantic core » (partiel, #984).ModifiéFonctionneApplication de bureau : l'écran Link graph suit la maquette GraphFilters (canevas et#975
panneau de filtres).
Le core ne renvoie pas encore tout le graphe de liens : le canevas affiche donc un état d'attente honnête, et les filtres restent inactifs tant que ce n'est pas le cas (#975).
#975 (ticket sur GitHub)ModifiéFonctionneOAuth GSC :
provider-auth --action cancel(CLI) etseo_provider_authaction=cancel#964(MCP) annulent un enregistrement de flux de consentement navigateur en attente ;
#964 (ticket sur GitHub)statussignale le flux en attente lorsqu'il existe. L'enregistrement du flux ne stocke aucun élément d'autorisation et expire au bout de dix minutes. C'est la première tranche de #964 : pas encore de démarrage ni de callback navigateur, donc aucun flux n'est créé par l'interface publique, et un accès connecté n'est toujours jamais revendiqué.ModifiéFonctionne
crawl-site --urls/--urls-file --projectenregistre désormais le crawl par liste d'URL#962comme exécution observée (
#962 (ticket sur GitHub)mode=list) : il apparaît dansproject-observeetproject-scans, avec son artefact dans le dossierscans/du projet. Un collecteur en échec est notéfailed; la progression et la reprise des exécutions par liste ne sont pas encore couvertes (#962).AjoutéFonctionneAjout d'un plan structuré des playbooks :
skill-listetscenario-listrenvoient les#955étapes ordonnées de chaque playbook, et
#955 (ticket sur GitHub)skill-show/scenario-showajoutent ses titres et ses étapes, afin qu'un agent lise la forme de la méthode sans analyser le markdown (#955). Nouvelle commande CLIscenario-listet nouvel outil MCPseo_scenario_list.ModifiéFonctionne
scan-content-searchaccepte--kind regex(MCPkind="regex") pour chercher dans les#954documents conservés à l'aide d'une expression régulière Python. Les expressions invalides sont refusées avant le scan ; un document qui dépasse le budget de temps de la regex est signalé comme indisponible, jamais comme absent.
#954 (ticket sur GitHub)ModifiéFonctionneLes appels DataForSEO dont la réponse est perdue (erreur réseau après l'envoi de la#949
requête) sont journalisés avec cost_unknown…
Les appels DataForSEO dont la réponse est perdue (erreur réseau après l'envoi de la requête) sont journalisés avec
#949 (ticket sur GitHub)cost_unknownetcharge_status: unknown, afin quespend-reportles liste comme incertains au lieu de les compter comme un appel mesuré à coût nul (#949).AjoutéFonctionneNouvelle commande
scan-structured-blocks(CLI) et outil MCP#948seo_scan_structured_blocks…Nouvelle commande
#948 (ticket sur GitHub)scan-structured-blocks(CLI) et nouvel outil MCPseo_scan_structured_blocks: pour une URL conservée, décrit chaque bloc JSON-LD (sa ligne source, son statutvalid/errors/warnings/parse_error, ses types, une liste à plat de ses propriétés, le JSON source borné et les erreurs du graphe Schema.org). Fonctionne hors ligne, sur le corps de page complet conservé uniquement ; aucune requête réseau et aucune modification des artefacts.ModifiéFonctionne
crawl-sitesans projet, qui écrit un artefact de scan SQLite, partage désormais son#942plafond de requêtes par hôte…
#942 (ticket sur GitHub)crawl-sitesans projet qui écrit un artefact de scan SQLite partage désormais son plafond de requêtes par hôte avec les autres crawls simultanés du même hôte, grâce à un planning privé stocké dans le répertoire d'état utilisateur (~/.config/seohead/.origin-pacing.sqlite). Auparavant, les crawls sans projet tournaient sans cadence entre processus.ModifiéFonctionneProfils crawl-site (#941) :
crawl-profile listaffiche les noms des profils enregistrés#941et…
Profils crawl-site (#941) :
#941 (ticket sur GitHub)crawl-profile listaffiche les noms des profils enregistrés, etcrawl-profile delete NAMEsupprime l'un d'eux. Les deux sorties sont en JSON, et les noms inconnus sont refusés avec la liste des profils enregistrés.ModifiéFonctionneProfils crawl-site (#941) :
--save-profile NAME --config FILEenregistre une config#941validée…
Profils crawl-site (#941) :
#941 (ticket sur GitHub)--save-profile NAME --config FILEenregistre une config de crawl validée comme profil nommé (refusée si elle contient des en-têtes d'identifiants) ;--profile NAMElance le crawl avec ce profil, et--sets'applique toujours par-dessus. Les profils sont stockés dans~/.config/seohead/crawl-profilesou dans le répertoire indiqué parSEOHEAD_CRAWL_PROFILES. La liste et la suppression des profils ne font pas partie de cette tranche.ModifiéFonctionne
compare.v1liste désormais les pages crawlées d'un seul côté (url_sets:#938
#938 (ticket sur GitHub)only_in_before,only_in_after, avec compteurs). Lorsque l'un des deux crawls est partiel,url_sets.unprovenvaut true, afin que l'absence ne soit pas présentée comme une preuve.ModifiéFonctionneChaque saut des chaînes de redirections et de canonicals enregistre son statut, la#930
Location suivante et son temps de réponse…
Chaque saut des chaînes de redirections et de canonicals enregistre son statut, la Location suivante (redirections) et son temps de réponse, afin que la chaîne d'une URL se lise à partir des preuves stockées, sans nouvelle requête (#930).
#930 (ticket sur GitHub)ModifiéFonctionneLes boucles de redirection des chaînes par URL se terminent par une étape marqueur#930
(
#930 (ticket sur GitHub){"url": ..., "loop": true}) qui n'est plus récupérée ; l'URL répétée reste ainsi visible dans les preuves stockées.final_urlpointe toujours sur le dernier saut réellement récupéré (#930).AjoutéFonctionneExposition des étapes de création et d'ingestion de scan du registre de remédiation via#926
la CLI et MCP :
#926 (ticket sur GitHub)remediation-create/seo_remediation_createcrée un registre vide, lié à un projet (producer_builddoit être un SHA Git complet de 40 caractères ; les chemins existants sont refusés), etremediation-ingest/seo_remediation_ingestingère un audit enregistré comme référence (baseline) et historique, sans relancer de crawl (une nouvelle ingestion de la même révision est idempotente). La logique de stockage ne change pas ; c'est la première tranche de #926.ModifiéFonctionne
crawl_siteet les exécutions de scan indiquenteffective_max_requests_per_secondet#925
#925 (ticket sur GitHub)warnings: un avertissement apparaît lorsque la cadence effective dépasse 2 req/s (le seuil est strictement supérieur à 2). La CLI affiche le même avertissement sur stderr ; le JSON sur stdout et les valeurs par défaut ne changent pas. Le pacer n'a pas été modifié.ModifiéFonctionneJournal d'événements structuré du projet (
events.jsonl) : événements en ajout seul,#923avec source, acteur et texte borné, lus du plus récent au plus ancien par pages, avec filtres de source et de texte. Exposé sous
#923 (ticket sur GitHub)project-event-append/project-event-pagedans la CLI et sousseo_project_event_append/seo_project_event_pagevia MCP (issue #923).AjoutéFonctionneAjout de la commande
ai-columnet de l'outil MCPseo_ai_column, première tranche dela colonne personnalisée IA (réf. #1012). Sur des pages de scan conservées et sélectionnées, ou sur des éléments normalisés fournis, elle prépare un plan borné à partir du title, des titres et d'un extrait de texte, avec une estimation de taille en caractères (sans prix appliqué) et le périmètre de consentement que l'agent appelant doit respecter avant tout transfert. Les valeurs par URL fournies sont validées par rapport au hash source de chaque page ; les valeurs manquantes, périmées, vides ou trop grandes sont signalées comme telles et jamais remplies. Un CSV facultatif, protégé contre l'injection de formules, exporte la colonne. SEOHEAD ne détient aucune clé fournisseur et n'effectue aucun appel à un modèle.
ModifiéFonctionneMutualisation des ouvreurs SQLite en lecture seule et en écriture dans
seohead.core.sqlite.Les lecteurs de stockage, l'importeur de scans, l'archive GSC, le stockage des sources, le stockage de jointure de preuves et les deux classes de contrôles ouvrent désormais leurs bases via un seul helper pour la connexion, la row factory et les PRAGMA. Les deux classes de contrôles ferment explicitement leurs connexions. Le comportement ne change pas ; la normalisation des chemins et l'ordre des PRAGMA restent à chaque point d'appel.
ModifiéFonctionneCorrections de fond issues d'une relecture des contrôles, des sources de données et de
l'import de preuves.
Changements de comportement voulus sur ces chemins : - Les chaînes de redirections d'exactement la limite de sauts (20 par défaut) sont désormais résolues au lieu d'être signalées comme non résolues. Le contrôle de boucle s'exécute avant le contrôle de limite, dans la résolution de chaîne comme dans le parcours des chemins de sauts. - Les contrôles de confiance et de citation EEAT comparent les hôtes de destination avec leur port (
netloc), comme la logique d'hôte des liens internes. Les sites avec ports explicites peuvent voir leurs résultats changer. - Les contrôles de mots-clés llms.txt font correspondre les termes ASCII comme des mots entiers, avec un pluriel facultatif :rapid,ShowcaseetToolbarne passent plus. Les radicaux en cyrillique continuent de correspondre comme sous-chaînes. - La capture de navigation fait correspondre le motifreloadàrefresh_navigation. Les motifs demandés non reliés, commepageBlockInterstitial, sont enregistrés comme observations de cadre que le validateur accepte. - La détection de technologies traite une implication niée (["not", X]) comme n'impliquant rien. - L'import de preuves rejette les noms de métriques déclarés en double, et la découverte Search Console utilise les lignes qu'elle reçoit. - Les nouvelles tentatives Yandex Cloud ne marquent une pause qu'entre deux tentatives. Les nouvelles tentatives de l'analyse des requêtes Yandex Webmaster attendent avant la tentative suivante. - La construction du schéma transmet sontimeoutau client HTTP (valeur par défaut inchangée). - Le stockage des reçus de livraison est créé en mode 0600 avant que SQLite ne l'ouvre. - Les exécutions du graphe sémantique sont sérialisées, car l'ensemble des mots vides est un état de module. Le runner sémantique signale les erreurs SQLite parok: falseau lieu de les lever. - Le parseur d'erreurs CrUX tolère les corps de réponse qui ne sont pas des objets. - Suppression d'une gardesqlite3 is Noneinatteignable dans le registre.ModifiéFonctionneApplication de bureau : un écran Backlinks dans la mise en page design-v2.
Il affiche l'état d'attente honnête tant que le core ne stocke pas de profil de liens du projet (#999) ; pas de lignes d'exemple, et les filtres de groupe sont désactivés pour la même raison.
ModifiéFonctionneApplication de bureau, écran Crawler : bannière des résultats temporaires avec l'adresse
sélectionnée et son statut…
Application de bureau, écran Crawler : ajout de la bannière des résultats temporaires avec l'adresse sélectionnée et le texte de statut (affichée seulement quand un crawl sans projet renvoie des résultats) ; déplacement des actions d'effacement et d'enregistrement comme projet dans un menu de débordement sous 1 000 px ; champ d'adresse et bande d'onglets de contrôle utilisables sur les largeurs étroites ; défilement de l'en-tête des résultats au lieu de compresser ses colonnes ; même marge droite de 8 px sur la barre de statut de l'interface, en thème sombre comme en thème clair.
ModifiéFonctionneApplication de bureau : écran des états bloquants, un par canevas (ErrorsCore).
Le core manquant, l'espace disque faible et l'absence d'accès en écriture sont affichés à partir de faits réels ; la version du core et l'intégrité des scans affichent la pastille neutre « unavailable in this core version » tant que le core ne les fournit pas.
ModifiéFonctionneApplication de bureau : le menu de ligne d'URL et le menu d'en-tête de colonne, les
groupes de la barre de menus Edit, Scan et Window, ainsi que les menus contextuels des constats et des tâches suivent le tableau Menus du canevas. Les entrées que le core ne fournit pas encore restent visibles mais désactivées, avec la raison dans l'infobulle.
ModifiéFonctionneApplication de bureau : l'écran « Export et rapports » suit le canevas ProjExport (jeu de
données, format…
Application de bureau : l'écran « Export et rapports » suit le canevas de conception ProjExport (choix du jeu de données et du format, carte de dossier, exports récents). Les jeux de données et formats que le core ne sait pas encore exporter (comparaison, tâches, PDF) sont affichés comme indisponibles ; le bouton d'exécution reste désactivé tant que l'export n'est pas branché (#1191).
ModifiéFonctionneNeutraliser les TAB et CR en tête dans les identifiants d'inventaire marketing et les CSV
de relecture…
Neutralisation des TAB et CR en tête dans les identifiants d'inventaire marketing et dans les CSV de relecture des brouillons de meta description, avec le même préfixe apostrophe que celui déjà appliqué à
=,+,-et@. Les deux protections utilisent désormaisseohead.core.tabular.neutralize_formula.ModifiéFonctionneRequêtes fournisseur avec identifiants (Arsenkin, Metrika, Yandex Cloud, IndexNow…
Les requêtes fournisseur qui transportent des identifiants (Arsenkin, Metrika, Yandex Cloud, IndexNow, Topvisor, destinations BI) ne suivent plus les redirections HTTP : un en-tête Authorization n'est donc jamais renvoyé à un autre hôte. Une redirection remonte désormais comme une erreur HTTP du fournisseur.
AjoutéFonctionneAjout d'un format de redirection
csv(lignessource,target,301, entre guillemets sinécessaire) dans…
Ajout du format de redirection
csv(lignessource,target,301, entre guillemets si nécessaire) àgenerate_ruleet à l'aide de la CLIredirects-generate. Le formatwordpress-redirectionn'est pas inclus, car sa syntaxe d'import n'a pas été vérifiée (#998).ModifiéFonctionneLe journal d'exécution (
runs.jsonl) fait désormais une rotation au-delà d'une taillelimite (5 Mo…
Le journal d'exécution (
runs.jsonl) fait désormais une rotation dès qu'il dépasse une taille limite (5 Mo par défaut ;SEOHEAD_RUN_LOG_MAX_BYTESpermet de la modifier). La génération précédente est conservée sous le nomruns.jsonl.1, et les lectures portent sur les deux fichiers, si bien que les recherches de réutilisation voient toujours les appels récents.ModifiéFonctionneMise en page de l'onglet Schema de la carte d'URL desktop selon la structure canvas
design-v2 (liste de blocs, arbre de blocs, contrôles). Le cœur ne renvoie que le nombre de blocs JSON-LD par URL : les colonnes affichent donc des états d'attente honnêtes, liés à l'issue 948 du cœur, et les comptes connus restent réels. Aucun bloc d'exemple n'est affiché.
9 octobre 2026
4 ajouté4 modifiéAjoutéFonctionneLibellés de terminal RU/EN selon la langue, bloc de progression du scan en cours,#951
sélection explicite…
Ajout de libellés de terminal RU/EN selon la langue choisie, d'un bloc de progression du scan en cours, d'une sélection explicite de scan et d'une sortie compacte de l'observateur.
#951 (ticket sur GitHub)ModifiéFonctionneLes scans sauvegardés s'ouvrent en lecture légère (#931).#931
#931 (ticket sur GitHub)scan-status,scan-url-detail,scan-link-inspectet l'observateur de projet (project-scans,project-observe) ne valident plus l'intégralité de l'artefact à chaque ouverture : ils vérifient l'identité, le schéma, la version et la ligne d'en-tête, prennent les compteurs en SQL (GROUP BYsurpages_status) et l'indiquent dans un champvalidationajouté ("light"ou"full"). La validation complète est lancée sur demande (full_validation: truesurscan-status, ou--input '{"input_path": "...", "full_validation": true}'en CLI), ou lorsque les mêmes octets (chemin, taille, date de modification, état WAL, hachage d'en-tête) ont déjà été entièrement validés dans le processus. Un scan qui échoue aux contrôles légers est refusé avec la même erreur qu'auparavant, et chaque chemin d'écriture ou d'enregistrement de preuves continue de valider intégralement.scan-url-detailexécute la recherche de la cible de redirection (sans index surresponses.effective_url_id) dans un budget de 0,5 s, et renvoieresponses.redirect_target_lookupavec la valeurskippedau lieu de parcourir une table volumineuse.AjoutéFonctionneÉtat MCP partagé et audité, avec révocation au démarrage et en direct des appels d'outils#929
; présence…
Ajout d'un état MCP partagé et audité, avec révocation au démarrage et en cours d'exécution des appels d'outils ; la présence sécurisée des clients est exposée via la CLI.
#929 (ticket sur GitHub)AjoutéFonctionneEnregistrement de clients avec autorisation explicite : aperçus, écritures atomiques,#929
sauvegardes…
Ajout de l'enregistrement de clients avec autorisation explicite : aperçus, écritures atomiques, sauvegardes vérifiées et restauration sans conflit.
#929 (ticket sur GitHub)AjoutéFonctionneAjout de
scan-url-query(MCPseo_scan_url_query) : lecture seule, filtrée, triée et#927paginée côté serveur, de l'ensemble de la table des pages d'un scan enregistré. Elle renvoie
#927 (ticket sur GitHub)total,filtered_total(nullavecfiltered_total_state: cappedlorsque le décompte dépasse son budget de temps), une projection de colonnes, les libellés de couverture du scan, le formatseohead.scan-url-query.v1et unreason_codelisible par machine pour chaque échec. Au maximum 200 lignes par appel. Le tri parurl,status_code,page_ordinalouurl_idutilise les index ; toute autre colonne exige un filtre qui laisse au plus 100 000 lignes (sinonsort_not_indexed). Le scan est ouvert avec une vérification légère de l'identité et de la génération du schéma, et non avec une validation complète, et il n'est jamais modifié.ModifiéFonctionne
scan-link-inspect --view links(MCPseo_scan_link_inspect) pagine les liens sortants#924ou…
#924 (ticket sur GitHub)scan-link-inspect --view links(MCPseo_scan_link_inspect) pagine les liens hypertextes sortants ou entrants d'une URL, recherchée par adresse et non par identifiant de document : ancre,relavec les indicateurs nofollow/sponsored/ugc, lien interne ou externe, position enregistrée, attributtargetet code HTTP de l'autre extrémité (nullsi cette page n'a pas été scannée). Filtres par type de lien, suivi, classe de statut et sous-chaîne ;offset/limitjusqu'à 200 ;total/filtered_totalsont exacts ou signalés comme plafonnés. Le scan est ouvert en lecture seule sans validation complète, et les échecs portent unreason_codelisible par machine (url_not_found,scan_not_available,url_not_scanned,links_not_retained). Les vues et champs existants restent inchangés.ModifiéFonctionneRéorganisation du paquet sans couche de compatibilité :
seohead.serversdevientseohead.mcp…
Réorganisation de l'arborescence du paquet, sans couche de compatibilité :
seohead.serversdevientseohead.mcp,seohead.toolsdevientseohead.checks,seohead.botetseohead.remote_apipassent sousseohead.integrations, la CLI vit dans le paquetseohead.cli(avecseohead.cli.terminal_progress), les modules racine partagés (models,graph,filesystem,runlog,verification,canonical_policy,input_contracts,job_contracts,provider_matrix) passent dansseohead.core, et la provenance de build passe dansseohead._build.provenance.examples/etconfig.example.jsonsont déplacés versdocs/examples/;PROVENANCE.mdetTRADEMARKS.mdversdocs/legal/. Les commandesseoheadetsf-analyzerne changent pas, mais les imports Python qui utilisent les anciens chemins de modules doivent être mis à jour.ModifiéFonctionneRéécriture du README : installation, trois parcours de démarrage rapide (du crawl natif
au rapport…
Réécriture du README autour de l'installation, de trois parcours de démarrage rapide (du crawl natif au rapport, exports Screaming Frog hors ligne, projet et agent), d'une carte des fonctionnalités par domaine, d'un aperçu de la CLI, de la configuration MCP pour Claude Code et Claude Desktop, de l'application de bureau compagnon et de la frontière entre mesuré et non mesuré, avec de vraies captures du terminal sous
docs/assets/, réalisées à partir d'une fixture QA locale et de la fixture d'export synthétique. Ajout dedocs/CLI.md, qui regroupe chaque commande enregistrée par tâche avec ses annotations réseau, écriture et payante (un test de dérive échoue désormais si une commande manque), et dedocs/WORKFLOWS.md, qui documente les commandes workflow-run, inbox et monitor-record, jusque-là seulement référencées.
8 octobre 2026
1 ajouté10 modifié1 corrigéCorrigéFonctionneComptage des octets HTML/DOM conservés local à la transaction et incrémental, au lieu de…#914
Le comptage des octets HTML/DOM conservés reste local à la transaction et incrémental, au lieu de reparcourir tous les corps stockés pour chaque réponse unique. Les instances d'écriture rouvertes reconstruisent le compteur une seule fois ; la déduplication, le rollback, les savepoints et les limites de conservation des corps restent intacts.
#914 (ticket sur GitHub)ModifiéFonctionneLe reporting BI garde à l'état inconnu les états de collecte fournisseur non vérifiés et#833
les indicateurs…
Le reporting BI maintient à l'état inconnu les états de collecte fournisseur non vérifiés et les indicateurs de qualité dans les métriques, la couverture et les manifestes ; un échantillonnage, un seuillage ou une troncature confirmé est considéré comme partiel. Les cohortes recherche contre sessions exigent une qualité de collecte explicite, des mesures de comptage non négatives et des périmètres de source qualifiés, et conservent pour chaque axe l'identité de la source, l'attribution, le périmètre de recherche et la politique calendaire. Le paquet de reporting synthétique reste hors ligne ; l'acceptation native dans Sheets et Looker reste distincte.
#833 (ticket sur GitHub)ModifiéFonctionneLes scans natifs SQLite acceptent
max_urls=0pour collecter jusqu'à épuisement de la#818file…
Les scans de site natifs SQLite acceptent
#818 (ticket sur GitHub)max_urls=0pour collecter jusqu'à épuisement de la file, etmax_depth=-1pour désactiver la limite de profondeur. Les budgets de temps et HTTP restent configurables séparément, les garde-fous de stockage ainsi que l'interruption et la reprise restent actifs, et les routes matérialisées héritées refusent une population d'URL illimitée.ModifiéFonctionneLes scans limités aux sitemaps conservent aussi la limite d'URL désactivée, au lieu de#818
refuser…
Les scans limités aux sitemaps conservent eux aussi la limite d'URL désactivée, au lieu de refuser leur premier membre déclaré lorsque la limite configurée vaut zéro.
#818 (ticket sur GitHub)ModifiéFonctionneLes débits explicites finis au-dessus de 2 requêtes/s atteignent désormais la voie de#818
cadencement partagée…
Les débits de requêtes explicites, finis et supérieurs à la valeur par défaut de 2 requêtes/s atteignent désormais la voie de cadencement partagée par origine de projet, en plus du limiteur par scan. Les scans parallèles réservent un seul calendrier commun par hôte au lieu de multiplier son débit.
#818 (ticket sur GitHub)ModifiéFonctionneL'interruption de l'analyse native renvoie un résultat explicite, reprenable, et conserve#754
les pages…
L'interruption de l'analyse native renvoie désormais un résultat explicite « interrompu et reprenable » et conserve les pages capturées, au lieu d'afficher une trace KeyboardInterrupt.
#754 (ticket sur GitHub)ModifiéFonctionneRégénération du catalogue de projets packagé après la mise à jour du scénario du provider#754
ModifiéFonctionneRejet des lignes de réponse Bing Webmaster mal formées, au lieu de signaler un résultat#753
vide complet…
Rejeter les lignes de réponse Bing Webmaster mal formées au lieu de signaler un résultat vide complet. Conserver les données brutes de réponse, les lignes sources et les comptes exacts renvoyés dans les preuves fournisseur enregistrées. Les réponses de comptage de liens conservent leur enveloppe de pagination documentée et signalent comme partielles les populations sur plusieurs pages lorsque seule une page demandée a été collectée.
#753 (ticket sur GitHub)ModifiéFonctionneReconnaissance du fallback de compte de service Google partagé dans la disponibilité GA4…#753
Reconnaître le fallback existant de compte de service Google partagé dans la vérification de disponibilité et la découverte des identifiants du provider GA4. Les permissions du compte et l'accès aux cibles restent non vérifiés, les identifiants mal formés sont signalés explicitement, et la prise en charge du bearer OAuth est conservée sans exiger les deux sources.
#753 (ticket sur GitHub)ModifiéFonctionneLier les références de capacités headless à l'isolation des exécutions natives et à la#743
frontière publique de routage des crawls externes, en distinguant l'implémentation interne de la disponibilité CLI/MCP, sans modifier le comportement de crawl ni les revendications de capacité.
#743 (ticket sur GitHub)AjoutéFonctionnePagination bornée des exécutions terminées dans
project-observe(CLI et MCP), engardant…
Ajout d'une pagination bornée des exécutions terminées à
project-observe, en CLI et en MCP, tout en gardant visible chaque enregistrement d'exécution en cours stocké. Les pages conservent les identifiants d'exécution exacts et l'ordre d'admission inverse ; les compteurs et les métadonnées de rétention décrivent le magasin actuel de 100 enregistrements, et non l'historique complet. La lecture ne modifie jamais les états d'exécution stockés. Les entrées JSON de la CLI conservent leur pagination et leur périmètre de projet, sauf si un drapeau nommé explicite les remplace.ModifiéFonctionneGestion de Ctrl+C sur toute la transmission native après collecte, y compris l'ouverture
du scan…
Gérer Ctrl+C sur toute la transmission native après collecte : ouverture du scan sauvegardé, phases de suivi facultatives, analyse et finalisation. Les captures inachevées conservent leurs données ainsi que leur point de contrôle reprenable, et renvoient un résultat JSON interrompu explicite. Une interruption après le commit final préserve l'artefact terminé et indique sa disponibilité d'audit réelle. La reprise explicite termine le travail post-collecte inachevé, même avec une frontière de crawl vidée, en préservant les drapeaux d'omission de preuves existants et les entrées sitemap conservées.
7 octobre 2026
4 ajouté4 modifié1 corrigéAjoutéFonctionneFixture Linux jetable pour l'exécution réelle de workers SSH par clé, les téléchargements#754
API/TLS…
Ajouter une fixture Linux jetable pour l'exécution réelle de workers SSH par clé, les téléchargements API/TLS authentifiés et l'erreur ENOSPC du worker natif sur un tmpfs réservé aux jobs. Préserver la file et les preuves, et exiger un résultat explicite de non-réussite, sans rejeu, avant un nouveau job de récupération JS. Préciser les mesures locales du cycle de vie, par opposition aux preuves SSH et de confinement OS.
#754 (ticket sur GitHub)ModifiéFonctionneConservation des preuves JavaScript partielles dans les résultats de jobs distants, y#754
compris la correction…
Conserver les preuves JavaScript partielles dans les résultats de jobs distants, y compris la correction en lecture seule des anciens jobs terminés, sans modifier les artefacts conservés ni les budgets de rendu. Vérifier la sauvegarde et la restauration réelles de la file via le statut authentifié, les téléchargements et l'idempotence persistante ; la fixture de récupération à deux pages reste en rendu complet dans ses budgets finis existants.
#754 (ticket sur GitHub)ModifiéFonctionneRenvoyer le code de sortie 130 sans trace lorsque SIGINT interrompt la sortie JSON
finale, une fois le gestionnaire terminé. Les artefacts conservés restent inchangés ; un flux de sortie tronqué n'est jamais signalé comme réussi.
AjoutéFonctionneEnregistrer la stratégie de zone de contenu qui a produit les hachages conservés et les
comptes de tokens…
Enregistrer la stratégie de zone de contenu utilisée pour produire les hachages conservés et les comptes de tokens, plutôt que la stratégie de zone par défaut du parseur. Les sélecteurs explicites et les exclusions disposent désormais d'une provenance d'extraction cohérente, même lorsque le parseur de la page a utilisé sa région par défaut.
CorrigéFonctionneGarder les fichiers de staging SQLite natifs non publiés hors de l'historique des scans
et de la rétention…
Exclure de l'historique des scans et des candidats à la rétention les fichiers de staging SQLite natifs non publiés ; continuer à signaler les fichiers publiés invalides et autoriser les scans cachés ordinaires nommés par l'utilisateur.
AjoutéFonctionneCollecte SQLite native limitée aux sitemaps : un sitemap explicite définit la population
d'URL complète…
Ajout de la collecte SQLite native limitée aux sitemaps : un sitemap explicite définit l'intégralité de la population d'URL demandée, sans repli sur la page de départ, ni expansion par liens hypertextes, ni expansion vers les cibles de redirection. Les populations de sitemap vides, incomplètes ou dépassant le budget s'arrêtent explicitement, et la reprise réutilise la population conservée sans retélécharger le XML.
ModifiéFonctionneEnregistrements d'exécution distincts pour les captures natives simultanées et file
partagée par projet…
Conserver des enregistrements d'exécution distincts pour les captures natives simultanées et partager une porte d'envoi locale au projet, limitée à 2 requêtes/s, pour un même hôte public ; exposer aux observateurs le type de source, l'origine, l'empreinte de configuration et la limite de débit agrégée de chaque exécution.
AjoutéFonctionneRecherche littérale hors ligne sur le HEAD, le HTML, le texte du corps et le balisage
sélectionné par CSS…
Ajout d'une recherche littérale hors ligne sur le HEAD, le HTML, le texte du corps et le balisage sélectionné par CSS conservés, avec une couverture statique ou rendue explicite, des corps manquants marqués comme inconnus, des extraits expurgés facultatifs et une pagination vérifiée par intégrité des nouveaux paquets de résultats locaux.
ModifiéFonctionnePréservation des réglages propres d'un crawl Screaming Frog sauvegardé lors de l'export :
load-crawl…
Préserver les réglages propres d'un crawl Screaming Frog sauvegardé lors de son export : load-crawl n'injecte plus la configuration actuelle du spider. Les exécutions spider et list en direct continuent de recevoir leurs profils explicites par exécution.
6 octobre 2026
9 ajouté14 modifié9 corrigéModifiéFonctionneRésolution cohérente des hôtes virtuels loopback des tests de capture externe selon les#896
systèmes…
Faire résoudre de façon cohérente les hôtes virtuels loopback des tests de capture externe propres, quel que soit le système d'exploitation, sans modifier la politique réseau de production.
#896 (ticket sur GitHub)CorrigéFonctionneSaisie du terminal fonctionnelle au-delà du plafond de descripteurs de select, et#896
protection des notes…
Garder la saisie du terminal fonctionnelle au-delà du plafond de descripteurs de select, et empêcher Delete ou les séquences de contrôle non prises en charge d'annuler des notes non enregistrées.
#896 (ticket sur GitHub)ModifiéFonctionneConservation des événements de navigation du navigateur versionnés, avec omissions#826
bornées, état de capture…
Conserver les événements de navigation du navigateur versionnés, avec des omissions bornées, l'état de capture, la politique du moteur de rendu et la provenance des URL. Distinguer les redirections HTTP observées, la navigation Chromium par script, historique ou fragment, et les causes inconnues, sans affirmer de clics humains.
#826 (ticket sur GitHub)AjoutéFonctionneMêmes preuves exposées via render-check et via les rapports bornés et hors ligne de#826
scan-navigation…
Exposer les mêmes preuves via render-check et les rapports bornés et hors ligne de scan-navigation ; conserver les observations partielles lorsque le rendu échoue.
#826 (ticket sur GitHub)ModifiéFonctionneCompatibilité de rollback préservée en conservant les preuves de navigation dans des#826
réglages de rendu extensibles…
Préserver la compatibilité de rollback en conservant les preuves de navigation dans des réglages de moteur de rendu extensibles ; valider l'encodage de la préversion antérieure sans affaiblir les contrôles de provenance, et ne pas traiter les événements observés comme une politique de rendu.
#826 (ticket sur GitHub)CorrigéFonctionneGrandes populations de synthèse imbriquées, dont les histogrammes de profondeur, dans les#818
compagnons PDF…
Conserver les grandes populations de synthèse imbriquées, y compris les histogrammes de profondeur, dans les compagnons PDF complets, sans les imputer au budget borné des métadonnées d'affichage. Préserver les destinations nommées, les signets et les liens internes lors de la réécriture des liens portables du compagnon, en clonant le document PDF complet.
#818 (ticket sur GitHub)AjoutéFonctionneAjouter
pdf_policy="overview-v1"explicite pour les requêtes PDF audit.v2 conservées.#818La première page distingue le nombre total de la source, le nombre affiché et le nombre omis ; les préfixes d'affichage déterministes conservent les états et raisons de couverture complets et renvoient vers le JSON d'audit complet en flux et vers les compagnons CSV des constats, des pages et du périmètre. Un manifeste de hachages et de comptes enregistre la provenance et les limites de ressources. Les destinations existantes sont refusées, les exports sont mis en zone de transit, et le marqueur d'achèvement PDF n'est publié qu'après validation de tous les compagnons obligatoires. Le comportement PDF par défaut ne change pas : l'aperçu n'est jamais sélectionné silencieusement ni présenté comme un PDF exhaustif. Voir
#818 (ticket sur GitHub)docs/PDF_OVERVIEW.mdpour les bornes exactes et la sémantique en cas d'interruption brutale.AjoutéFonctionneOpération fournisseur GA4
page_viewsen lecture seule : vues par date, nom d'hôte et#782chemin visité…
Ajout de l'opération fournisseur GA4 en lecture seule
#782 (ticket sur GitHub)page_views, pour les comptes de vues par date, nom d'hôte et chemin visité, indépendamment des sessions sur les pages d'entrée. Réutilise les identifiants GA4 existants, le transport et le lecteur de rapports paginés avec des tentatives bornées, les issues de quota et de permission, les métadonnées explicites de fuseau horaire, d'échantillonnage et de seuil, ainsi que la liaison d'URL à l'origine déclarée. Les preuves fournisseur restreintes enregistrées se normalisent via la jointure et le pipeline BI existants ; un trafic manquant reste indisponible au lieu de devenir zéro.CorrigéFonctionneRafraîchissements de l'observateur bornés : couverture par site réutilisée, activité des#777
collecteurs…
Garder les rafraîchissements de l'observateur bornés en réutilisant la couverture par site, en projetant séparément l'activité en direct des collecteurs et en paginant les constats audit.v2, sans matérialiser les collections du graphe non liées.
#777 (ticket sur GitHub)ModifiéFonctionneChaque collecteur actif conservé dans un historique borné, fenêtres de compteurs mesurés#777
horodatées…
Conserver chaque collecteur actif dans un historique borné, publier des fenêtres de compteurs mesurés horodatées et distinguer les compteurs non mesurés de zéro. Diffuser la progression vérifiée de Screaming Frog tout en bornant les deux canaux de diagnostic.
#777 (ticket sur GitHub)AjoutéFonctionnePages en lecture seule pour la checklist, le détail des tâches et les scans conservés,#777
avec périmètre…
Ajout de pages en lecture seule pour la checklist, le détail des tâches et les scans conservés, plus un périmètre explicite des méthodes applicables et une projection de la boîte de réception la plus récente.
#777 (ticket sur GitHub)CorrigéFonctionneExécutions de sitemap explicitement associées gardées distinctes : compteurs en cours#777
inconnus…
Garder distinctes les exécutions de sitemap explicitement associées : les compteurs en cours restent inconnus, et les mesures finales comptent séparément les documents sitemap analysés, les URL déclarées, les erreurs et les troncatures.
#777 (ticket sur GitHub)ModifiéFonctionneReçus d'artefacts de checklist observés via des métadonnées d'identité de fichier#777
protégées…
Observer les reçus d'artefacts de checklist via des métadonnées d'identité de fichier protégées, sans recalculer le hachage des octets conservés à chaque rafraîchissement. Les lectures de couverture explicites conservent la vérification des octets ; les preuves modifiées ou héritées non vérifiées restent visibles comme obsolètes ou non vérifiées.
#777 (ticket sur GitHub)CorrigéFonctionneReçus de priorité historiques liés à leur périmètre d'origine quand de nouvelles tâches#777
sont ajoutées…
Garder les reçus de priorité historiques liés à leur périmètre d'éléments d'origine lorsque de nouvelles tâches sont ajoutées après la préparation, et valider chaque candidat de couverture complet avant la publication atomique. Les lectures de détail concurrent limitées à un périmètre préservent la vérification passive par métadonnées seules.
#777 (ticket sur GitHub)AjoutéFonctionneComparaison explicite des audits sauvegardés, sur disque, avec résultats NDJSON complets…#762
Ajout d'une comparaison explicite des audits sauvegardés, sur disque, avec résultats NDJSON complets, ordre déterministe, identités des sources, sommes de contrôle par fichier et conservation des constats avant et après. Les comparaisons audit.v2 volumineuses exigent un nouveau répertoire de sortie ; les identités en double et les collisions de correspondance d'URL échouent au lieu de perdre des lignes.
#762 (ticket sur GitHub)CorrigéFonctionneVérification ciblée audit.v2 bornée en présence de lignes en double, identité de scan#762
conservée…
Garder la vérification ciblée audit.v2 bornée lorsque des lignes en double sont présentes, conserver l'identité de scan validée dans les documents sélectionnés, et refuser les identités contradictoires ou les crawls postérieurs invalides comme preuve de correction.
#762 (ticket sur GitHub)AjoutéFonctionneCompression gzip en flux facultative pour les fichiers NDJSON compare.v2 complets…#762
Ajout d'une compression gzip en flux facultative pour les fichiers NDJSON compare.v2 complets, avec des octets compressés déterministes, des hachages exacts des fichiers compressés et des comptes décodés de lignes et d'octets. Ajout d'un lecteur borné qui valide les exports en clair ou gzip et rejette les flux endommagés ou les comptes de manifeste qui ne correspondent pas ; la sortie en clair reste le mode par défaut.
#762 (ticket sur GitHub)CorrigéFonctionneCompatibilité de comparaison de corpus inconnue si l'un des indicateurs de complétude est#762
absent…
Garder la compatibilité de comparaison de corpus inconnue lorsque l'un des deux indicateurs de complétude est absent, et avertir explicitement des corpus partiels connus. Conserver les identifiants et les raisons des contrôles ignorés ou désactivés aux côtés des écarts de constats, afin que des mesures manquantes ne puissent pas être présentées comme une correction vérifiée.
#762 (ticket sur GitHub)ModifiéFonctionneObservateur de projet du terminal : navigation lisible dans les tâches, méthodes, données#752
structurées…
Permettre à l'observateur de projet du terminal de parcourir les détails lisibles des tâches, méthodes, données structurées, scans, exécutions, boîte de réception et preuves conservées, avec des pages bornées et une couverture de périmètre honnête. Les lectures de stockage et les soumissions explicites de notes sortent de la boucle de saisie.
#752 (ticket sur GitHub)ModifiéFonctionneBrouillons de notes et d'objectifs conservés séparément pendant une session, édition au#752
curseur…
Conserver séparément les brouillons de notes et d'objectifs pendant une session, prendre en charge l'édition au curseur, conserver les collages trop longs sans troncature, afficher les reçus d'enregistrement dans toutes les mises en page, et imprimer les brouillons non enregistrés après une sortie normale du terminal pour récupération.
#752 (ticket sur GitHub)AjoutéFonctionneNavigation explicite de la couverture et des artefacts du site et des concurrents, site#752
sélectionné conservé…
Ajouter une navigation explicite de la couverture et des artefacts du site propre et des concurrents, conserver le site sélectionné à travers les pages de scan et de constats, afficher les suggestions récentes de concurrents issues des notes, et garder visible la progression de collecte mesurée dans les terminaux compacts. Les échantillons périmés ne s'animent plus comme une progression en direct.
#752 (ticket sur GitHub)AjoutéFonctionneInventaire versionné des capacités headless généré à partir des vrais réglages du crawler…#743
Ajout d'un inventaire versionné des capacités headless généré à partir des vrais réglages du crawler, avec des références d'implémentation, de CLI et MCP, de tests et d'écarts d'acceptation explicites.
#743 (ticket sur GitHub)AjoutéFonctionneBenchmark HTML/JavaScript optionnel sur origine propre, avec budgets figés de phase et de#743
mémoire…
Ajout d'un benchmark HTML/JavaScript optionnel sur origine propre, avec des budgets figés de phase, de mémoire de l'arbre de processus, de disque et de récupération. De vrais navigateurs créent les DOM mesurés ; le profil n'établit pas à lui seul la capacité JavaScript pour les très grands sites.
#743 (ticket sur GitHub)CorrigéFonctionneCorrection de l'argument de streaming non pris en charge du pont JavaScript natif et#743
préservation…
Corriger l'argument de streaming non pris en charge du pont JavaScript natif et préserver les compteurs de rendu déjà observés lors d'une annulation propre et d'une reprise.
#743 (ticket sur GitHub)ModifiéFonctionnelog-scan consomme les chemins de scan natifs conservés et les compagnons audit.v2 sans#711
JSON hérité…
Permettre à log-scan de consommer les chemins de scan natifs conservés et les compagnons audit.v2 sans matérialiser le JSON hérité. Diffuser les constats de cohérence, garder sur disque l'appartenance aux pages natives et la déduplication des constats, préserver le décompte exact des anomalies en ne limitant que les exemples. Signaler explicitement l'absence de trace native des décisions d'exclusion.
#711 (ticket sur GitHub)ModifiéFonctionneConstat lastmod de sitemap à l'échelle du site reconnu à l'origine nue du site mesuré,#711
sans page d'accueil…
Reconnaître un constat lastmod de sitemap à l'échelle du site, à l'origine nue du site mesuré, lorsqu'aucune page d'accueil n'a été crawlée. L'exception exige des preuves de synthèse du sitemap correspondantes et n'exempte ni les constats de pages, ni les hôtes sans rapport, ni les chemins cibles non pris en charge.
#711 (ticket sur GitHub)ModifiéFonctionneComparaison des scans audit.v2 conservés via une clé exacte et un index ordinal, sans
duplication…
Comparer les scans audit.v2 conservés via une clé exacte et un index ordinal, sans dupliquer les charges utiles des pages et des constats dans le stockage SQLite temporaire. Les lignes complètes, les exports déterministes en clair ou gzip, les identités des sources et les entrées héritées restent pris en charge. Les lecteurs d'audit fixent l'instantané validé via des lectures de lignes indexées.
ModifiéFonctionneAnnuler les sorties de comparaison en cas d'échec tardif de publication et refuser les
destinations concurrentes…
Annule les membres de sortie de comparaison détenus en cas d'échec tardif de publication, et refuse les destinations apparues en parallèle. Le manifeste reste le marqueur final de validation du paquet.
ModifiéFonctionneDécrire le point d'entrée public : ingénierie SEO configurable avec agents et liens vers
les méthodes…
Le point d'entrée public présente désormais l'ingénierie SEO configurable avec agents et relie chaque capacité à ses méthodes, à ses entrées et aux limites de ses preuves. Les consignes obsolètes sur le natif et SF, la couverture des projets et la remédiation sont corrigées. Le classeur développeur rédigé par les agents est documenté à part des rapports factuels, avec les critères d'acceptation revus et les liens vers les sources.
CorrigéFonctionneRétablir la parité du rapport humain entre les entrées audit.v2 streamées et le JSON
d'audit équivalent…
Le Markdown utilise désormais un seul moteur de rendu incrémental, y compris les explications des contrôles indisponibles, les enregistrements d'exclusion et les notes de couverture finales. Le XLSX partage une mise en page unique des métadonnées et écrit les constats et les pages dans des feuilles à écriture seule bornée, en conservant toutes les feuilles, colonnes, filtres, graphiques et compteurs de sources. Les constats supprimés conservés peuvent être rejoués sans matérialiser leur collection, et les URL de run manquantes utilisent le même repli sur la première page.
ModifiéFonctionneÉtiqueter la provenance des audits PDF indépendamment du schéma d'audit partagé : crawler
natif…
Les audits PDF étiquettent leur provenance indépendamment du schéma partagé : crawler natif, exports Screaming Frog ou collecteur SF explicitement identifié, et collecteur inconnu. Un audit natif utilisant le schéma SF Analyzer ne reçoit plus d'en-tête Screaming Frog.
ModifiéFonctionneÉcrire en flux les lignes de portée des constats supprimés en CSV au lieu de les
accumuler…
Les lignes de portée des constats supprimés sont écrites en CSV au fil de l'eau, sans accumuler en mémoire leurs explications rendues. Les exports complets d'exclusions conservent leur ordre et leurs faits sources, tout en gardant une allocation bornée pour de grandes collections d'exclusions conservées.
5 octobre 2026
1 modifiéModifiéFonctionneRéconcilier chaque ligne de lacune de couverture à partir de sa vraie cellule Mode,#830
valider les références…
Chaque ligne de lacune de couverture est réconciliée à partir de sa cellule Mode réelle, les références de contrôle et d'outil couvertes sont validées contre les registres publics, et les priorités sont générées à partir des lignes non résolues.
#830 (ticket sur GitHub)
4 octobre 2026
56 ajouté75 modifié5 corrigéAjoutéFonctionneAjouter une projection hors ligne de progression GSC marque/hors marque, avec alias#880
Unicode explicites…
Ajout d'une projection hors ligne de la progression GSC marque/hors marque, avec alias Unicode explicites, CTR pondéré compatible et tranches de position moyenne étiquetées.
#880 (ticket sur GitHub)AjoutéFonctionneAjout d'un regroupement optionnel des tâches via
check_assignmentpour les URL de#878modèles et composants…
Ajout d'un regroupement optionnel des tâches via
#878 (ticket sur GitHub)check_assignment, pour les correspondances d'URL de modèles et composants déclarées ou les segments de crawl validés. Les éléments groupés conservent le nombre d'occurrences et d'URL, des preuves plafonnées, la provenance du regroupement, le périmètre de vérification et les états déclaré, candidat, confirmé ou non assigné.AjoutéFonctionneAjouter des cartes de comparaison de versions
url-correspondence.v1avec origines et#877paires d'URL…
Ajout, en option, de cartes de comparaison de versions
#877 (ticket sur GitHub)url-correspondence.v1avec origines et paires d'URL déclarées, refus des collisions, provenance des faits de page et sortierelease_review.v1.AjoutéFonctionneAjouter une projection hors ligne de cohortes de publication, qualifiée par preuves, avec#876
provenance des dates…
Ajout d'une projection hors ligne de cohortes de publication, qualifiée par les preuves : provenance des dates, inventaire des auteurs, observations étiquetées des fournisseurs et fenêtres d'âge optionnelles.
#876 (ticket sur GitHub)AjoutéFonctionneAjouter un inventaire hors ligne des CTA et formulaires intégrés pour le HTML fourni,#875
préservant les occurrences…
Ajout d'un inventaire hors ligne des CTA et formulaires intégrés pour le HTML fourni, préservant les occurrences, avec cibles manquantes nullables, sorties JSON et CSV bornées et couverture explicite des corps incomplets.
#875 (ticket sur GitHub)ModifiéFonctionneConserver la classification des timeouts distants tout en masquant les erreurs de#866
transport, et rejouer…
Conservation de la classification des timeouts distants tout en masquant les erreurs de transport, et rejeu sûr des manifestes de proxy direct pour la vérification ciblée.
#866 (ticket sur GitHub)ModifiéFonctionneLire les preuves d'attribution dans le graphe de liens SQL conservé, préserver#866
l'attribution nulle explicite…
Les preuves d'attribution sont lues dans le graphe de liens SQL conservé, l'attribution nulle explicite est préservée à l'export, et les contrôles des liens externes en attente sont conservés lors de la reprise.
#866 (ticket sur GitHub)ModifiéFonctionneRégénérer les catalogues intégrés et les contrats de rapport ; installer le validateur#866
PDF avec l'extra all…
Régénération des catalogues intégrés et des contrats de rapport. Le validateur PDF s'installe avec l'extra « all », et les tests à faux navigateur ne dépendent plus du Chrome local sélectionné.
#866 (ticket sur GitHub)AjoutéFonctionneAjouter un plan de chargement BigQuery optionnel et hors ligne, exposant des tables#837
versionnées par schéma…
Ajout d'un plan de chargement BigQuery optionnel et hors ligne, qui expose des tables versionnées par schéma, les exigences de facturation et les prérequis d'autorisation explicites, sans accès au cloud.
#837 (ticket sur GitHub)AjoutéFonctionneAjouter un transport REST BigQuery à liste blanche, avec jobs de chargement de staging#837
déterministes…
Ajout d'un transport REST BigQuery à liste blanche, avec jobs de chargement de staging déterministes, réconciliation du nombre de lignes et publication atomique documentée par table, plutôt qu'une fausse transaction inter-tables.
#837 (ticket sur GitHub)ModifiéFonctionneExiger une approbation
cost_authorizeddistincte, gérée par l'hôte, pour BigQuery avant#837tout jeton…
Exige une approbation
#837 (ticket sur GitHub)cost_authorizeddistincte, gérée par l'hôte, pour BigQuery, avant tout jeton, toute vérification de jeu de données ou toute écriture de staging.AjoutéFonctionneAjouter un générateur local et validé de lien de copie Looker Studio pour le pack#836
synthétique à cinq pages…
Ajout d'un générateur local et validé de lien de copie Looker Studio pour le pack de reporting synthétique en cinq pages. Il exige un ID de rapport d'origine et des alias de source Sheets ou BigQuery exacts, sans publier de rapport ni inventer de modèle.
#836 (ticket sur GitHub)AjoutéFonctionneAjouter la fixture synthétique de feuilles versionnée#836
(
examples/reporting-pack/worksheets/)…Ajout de la fixture synthétique de feuilles versionnée (
#836 (ticket sur GitHub)examples/reporting-pack/worksheets/) : un CSV par jeu de données BI, plus le manifeste, généré de façon déterministe à l'octet près à partir deexamples/audit.jsonet de deux entrées synthétiques GSC et GA4. Elle illustre les états suivants : zéro mesuré, supprimé ou manquant, externe seul, non rattachable, contrôle ignoré, jeu de données indisponible et quadrant incomplet. Sa reproductibilité est vérifiée parscripts/generate_reporting_pack_worksheets.py --check.AjoutéFonctionneAjouter
examples/reporting-pack/SETUP.md: le guide mécanique côté propriétaire pour#836importer…
Ajout de
#836 (ticket sur GitHub)examples/reporting-pack/SETUP.md: le guide mécanique, côté propriétaire, pour importer les six feuilles dans Google Sheets, relier les alias de sources de données réservés, construire les cinq pages du blueprint avec les contrôles et champs calculés déclarés, activer l'autorisation de copie et vérifier une copie neuve. Le modèle Looker Studio natif reste une étape externe, à autoriser par le propriétaire. Aucun ID de rapport, lien ni écriture Google n'est inclus.ModifiéFonctionneConserver les noms de partitions, les compteurs et les sommes de contrôle du paquet BI#836
généré, afin que la fixture…
Le paquet BI généré conserve désormais ses noms de partitions, ses compteurs et ses sommes de contrôle, pour que la fixture de feuilles passe le contrôle préalable Sheets existant et les consommateurs d'export filtré. Les champs d'alias de l'API Linking sont documentés séparément, et la vérification d'une copie neuve via l'URL générée est exigée, sans réparation manuelle des sources.
#836 (ticket sur GitHub)ModifiéFonctionneRejeter les alias d'API Linking en double après normalisation des espaces et renvoyer les#836
alias exacts…
Rejet des alias d'API Linking en double après normalisation des espaces, et renvoi des alias exacts utilisés dans les paramètres d'URL. Définition d'identifiants de champs d'exécution partagés, de contrôles de date fournisseur groupés et de graphiques de couverture qui excluent les populations qui se chevauchent. La création du modèle natif et la vérification d'une copie neuve restent des exigences d'acceptation externes.
#836 (ticket sur GitHub)AjoutéFonctionneAjouter des lignes de cohortes d'URL explicites et qualifiées par preuves aux paquets BI#835
locaux, y compris…
Ajout de lignes de cohortes d'URL explicites et qualifiées par preuves aux paquets BI locaux, y compris des quadrants Search Console et GA4 sûrs pour les zéros mesurés, uniquement pour des observations complètes, compatibles et un-à-un.
#835 (ticket sur GitHub)ModifiéFonctionneCalculer les parts observées de liens entrants à partir des pages liantes uniques du#835
périmètre…
Calcul des parts observées de liens entrants à partir des pages liantes uniques du périmètre, avec extraction HTML terminée. Les occurrences répétées sont conservées séparément et les populations partielles sont étiquetées.
#835 (ticket sur GitHub)AjoutéFonctionneAjouter des paquets BI hors ligne, typés et déterministes pour les scans, audits et#834
preuves fournisseurs…
Ajout de paquets BI hors ligne, typés et déterministes, pour les scans et audits enregistrés et les preuves fournies par les fournisseurs, avec bornes de partitions, états nuls, conservation des lignes et cellules CSV protégées contre l'injection de formules.
#834 (ticket sur GitHub)ModifiéFonctionnePréserver les constats des fichiers compagnons audit.v2 bornés, joindre jusqu'à 1 000 000#834
d'URL de scan…
Conservation des constats issus des fichiers compagnons audit.v2 bornés, jointure de jusqu'à 1 000 000 d'URL de scan conservées via un artefact SQLite durable de preuves, et diffusion de cet artefact vers BI sans matérialiser les listes de pages ni d'URL correspondantes. Cela ne livre pas de modèle Looker Studio copiable.
#834 (ticket sur GitHub)ModifiéFonctionneProjeter les groupes de constats, les candidats de quadrants fournisseurs et les sources#834
de liens entrants…
Projection des groupes de constats, des candidats de quadrants fournisseurs et des sources de liens entrants éligibles, via un index SQLite jetable et borné. Les dimensions dynamiques fournisseurs restent valides dans les exports sélectionnés et dans les plans hors ligne Sheets et BigQuery. Le nombre de partitions et la consommation disque sont bornés.
#834 (ticket sur GitHub)ModifiéFonctionnePréserver les URL cibles et messages des constats d'audit natifs comme colonnes BI typées…#834
Les URL cibles et messages des constats d'audit natifs sont désormais des colonnes BI typées, et non plus seulement présents dans le JSON source conservé. Les sélections d'URL exactes conservent ainsi les constats concernés.
#834 (ticket sur GitHub)AjoutéFonctionneAjouter
scan-fragment-links(seo_scan_fragment_links) et le contrôle d'audit#827BROKEN_BOOKMARK…Ajout de
#827 (ticket sur GitHub)scan-fragment-links(seo_scan_fragment_links) et du contrôle d'auditBROKEN_BOOKMARK: chaque<a href>avec fragment, dans le HTML ou DOM complet conservé, est résolu contre les id d'éléments et les cibles<a name>du document de destination, selon l'algorithme WHATWG de défilement vers le fragment. Le fragment sérialisé est d'abord comparé aux id et aux noms, puis la valeur décodée en pourcentage et en UTF-8 est comparée aux deux.+reste un plus littéral, et#ou#toprenvoient au haut du document. Les représentations statique, rendue et de fragment hérité sont évaluées séparément, les règles de redirection et de<base href>sont respectées, et une chaîne de requête différente désigne un autre document. Les corps manquants, tronqués, non pris en charge, en échec ou épuisés par le budget sont signalés comme des ignorés nommés, jamais comme des constats d'erreur. Les exports Screaming Frog et les imports hérités indiquent le contrôle comme indisponible, au lieu de prétendre qu'il s'est bien déroulé. Les résultats sont bornés et paginés de façon déterministe (total,returned,offset/limit,next_offset, état de couverture), identiques via la CLI, le MCP et le résumé d'audit des scans conservés. Rien n'est récupéré : l'évaluation est en lecture seule et hors ligne.ModifiéFonctionneValider les liens de retour hreflang natifs par rapport aux déclarations cibles#825
conservées complètes…
Validation des liens de retour hreflang natifs par rapport aux déclarations cibles conservées complètes, en distinguant un retour manquant avéré d'une cible non explorée ou dont le corps est indisponible. Signalement des alternatives noindex observées, conservation des libellés bruts et des identités d'URL normalisées avec contexte de doublon ou de conflit, et marquage des relations de population partielle comme non mesurées. Les surfaces d'audit, CLI, MCP et rapport existantes portent les mêmes constats, sans récupération supplémentaire.
#825 (ticket sur GitHub)AjoutéFonctionneAjouter
CANONICAL_TARGET_ERRORpour signaler les cibles canonical récupérées en 4xx/5xx…#824Ajout de
#824 (ticket sur GitHub)CANONICAL_TARGET_ERRORpour signaler les cibles canonical récupérées qui renvoient un 4xx ou un 5xx, avec preuves de la source, de la cible, de la réponse et de l'indexabilité. Les cibles inconnues ou au statut incomplet restent indisponibles. Les variantes 2xx connues lèvent le verdict d'erreur, et les cibles 3xx restent sousCANONICAL_TO_REDIRECT.AjoutéFonctionneAjouter
CANONICAL_HOMEPAGE_GROUPpour les groupes d'au moins trois pages indexables#824récupérées…
Ajout de
#824 (ticket sur GitHub)CANONICAL_HOMEPAGE_GROUPpour les groupes d'au moins trois pages récupérées et indexables, réparties sur au moins deux sections de chemin, avec des titres et H1 distincts, et qui se canonicalisent vers la page d'accueil récupérée. Les pages d'accueil auto-référencées, les URL avec requête, la pagination reconnaissable, les motifs de pagination ou de filtre configurés, les preuves d'accueil incomplètes et les crawls natifs partiels ne produisent aucun constat à l'échelle du site.AjoutéFonctionneAjouter des preuves objectives d'auteur, de date et de pages de confiance aux audits#823
ciblés (issue #823)…
Ajout de preuves objectives sur l'auteur, les dates et les pages de confiance aux audits ciblés (issue #823) : huit contrôles de gravité « notice » dans
#823 (ticket sur GitHub)seohead/sf/core/eeat.py, qui signalent ce qu'un crawl peut observer, et jamais un score E-E-A-T.NO_AUTHOR_BYLINEetNO_CONTENT_DATESne se déclenchent que sur les pages de type contenu (type d'article JSON-LD,og:type=article, élément<article>ou chemin de contenu conventionnel). Ils nomment les porteurs lus (meta_author,rel_author,itemprop=author, classebyline,<time datetime>,author/datePublished/dateModifieden JSON-LD, chemin d'URL de type date), chacun avec une confiance déclarée.MISSING_ABOUT_PAGE,MISSING_CONTACT_PAGE,MISSING_PRIVACY_POLICYetMISSING_TERMS_PAGErenvoient l'un de ces états :found_indexable(silencieux),found_non_indexable,found_error,discovered_not_crawledounot_discovered. Ainsi, une page qui existe sans être servie ne se lit jamais comme une page que personne n'a liée.FEW_CITATIONSne signale que ce que les positions des liens permettent de prouver : un lien sortant dans le corps du texte, ou un compte déclaré à zéro.YMYL_REVIEW_CANDIDATEsignale les correspondances déterministes de mots entiers dans le chemin ou le titre, pour relecture humaine, jamais une classification. Le non-mesuré reste distinct de l'absent à tous les niveaux : une page dont le corps n'a jamais été analysé gardetrust_signalsà NULL et est comptée dans la raison d'ignorance.summary.trust_evidenceenregistre le périmètre évalué, l'état des pages par type et une mise en garde de crawl partiel pour les verdictsnot_discovered.ModifiéFonctionneClasser les p75 de terrain CrUX de la fenêtre actuelle (LCP, INP, CLS) selon des seuils#822
officiels versionnés…
Classement des p75 de terrain CrUX de la fenêtre actuelle (LCP, INP et CLS) selon des seuils officiels versionnés, en conservant la portée URL ou origine, le facteur de forme et la période de collecte. Les preuves de terrain manquantes, incomplètes ou en échec restent indisponibles ou partielles.
#822 (ticket sur GitHub)AjoutéFonctionneAjouter un échantillonnage d'URL explicite et borné avec cache privé optionnel, et#822
accepter les preuves CrUX…
Ajout d'un échantillonnage d'URL explicite et borné, avec cache privé optionnel, et acceptation des preuves CrUX enregistrées dans les audits et rapports de site, sans requête automatique vers le fournisseur. Les octets de réponse et de cache sont bornés. Les nombres d'échantillons enregistrés incohérents et les enregistrements d'URL répétés sont rejetés, au lieu d'afficher une couverture de terrain complète.
#822 (ticket sur GitHub)ModifiéFonctionneDocumenter la frontière du CLI Docker one-shot, séparément du service API distant et#820
worker optionnel
Documentation de la frontière actuelle du CLI Docker en mode one-shot, séparée du service API distant et du worker optionnel, construits explicitement.
#820 (ticket sur GitHub)AjoutéFonctionneAjouter la recette d'exécution SSH à espace de travail persistant, et expliquer que#820
l'état de sortie du CLI…
Ajout de la recette d'exécution SSH à espace de travail persistant, et explication que l'état de sortie et les artefacts du CLI ne constituent pas une vérification de santé de service HTTP.
#820 (ticket sur GitHub)ModifiéFonctionneAdmettre les configurations explicites de crawl natif SQLite jusqu'à 1 000 000 d'URL,#818
avec un budget distinct…
Admission des configurations explicites de crawl natif SQLite jusqu'à 1 000 000 d'URL, avec un budget distinct de 2 000 000 de requêtes HTTP et le profil synthétique existant, limité au stockage. La CLI, le MCP, la file distante et les workers respectent les mêmes budgets déclarés. Les API matérialisées héritées (legacy et listes) conservent une borne de sécurité explicite de 50 000 URL. Les robots de liste, les nouvelles tentatives, les redirections et les sauts canoniques partagent désormais leur budget de requêtes. Les audits natifs et la réanalyse hors ligne passent par le chemin audit.v2 borné, sans l'ancien refus de pont à 10 000 pages ou 20 000 formulaires. Les compteurs du writer transactionnel évitent de rescanner la frontière croissante à chaque lot. Un pilote d'acceptation manuel enregistre les identités sources chargées, l'interruption et la reprise, ainsi que les consommateurs en aval. L'admission n'est pas en soi la preuve d'une acceptation complète du million d'URL.
#818 (ticket sur GitHub)ModifiéFonctionneExécuter chaque étape d'acceptation native manuelle sous un surveillant de ressources de#818
sous-processus…
Chaque étape d'acceptation native manuelle s'exécute sous un surveillant de ressources de sous-processus, conserve les artefacts interrompus, vérifie chaque ligne et la couverture des tâches en CSV et XLSX, et exerce séparément le HTTP en boucle locale détenue et protégée, avec un rythme par défaut inchangé.
#818 (ticket sur GitHub)ModifiéFonctionneAutoriser des durées de crawl distant explicitement permises allant jusqu'à 14 jours, en#818
conservant l'heure par défaut…
Autorisation de durées de crawl distant explicitement permises jusqu'à 14 jours, en conservant le délai par défaut d'une heure et les budgets indépendants de projet, de requêtes, de disque, de bail et de rendu. Une cible d'un million de pages peut ainsi tenir dans son débit de requêtes poli inchangé. Les valeurs au-delà du plafond de durée et les types invalides échouent avant toute collecte.
#818 (ticket sur GitHub)ModifiéFonctionneLa réanalyse hors ligne utilise une échéance propre à chaque opération SQLite execute et#818
fetch. L'interruption et l'annulation des requêtes sont préservées, sans imputer à l'échéance initiale de validation d'ouverture du scan le temps d'analyse Python ni les écritures dérivées.
#818 (ticket sur GitHub)ModifiéFonctionne
crawl-site --out-dirhérité conserve désormais les preuves de pages, liens et#817formulaires dans des JSONL annexes…
Le mode hérité
#817 (ticket sur GitHub)crawl-site --out-dirconserve désormais les preuves de pages, liens et formulaires dans des fichiers JSONL annexes en ajout seul, pendant la collecte et la reprise, au lieu de garder des listes dupliquées en mémoire. Les points de contrôle vérifient les compteurs des annexes. Les anciens points de contrôle v4 à formulaires inline migrent sans perdre les constats de formulaires. Le pont d'audit existant nomme ses bornes de matérialisation pages, formulaires et liens, et préserve les preuves collectées lorsqu'un audit est indisponible. Un rapport antérieur est conservé sous un nom de fichier obsolète, au lieu d'être présenté comme le run courant. L'escalade JavaScript native lit les enregistrements de pages via une vue SQLite réitérable, au lieu de reconstruire tous les objets de pages. Le streaming complet des gros audits reste suivi dans #816 et #817.ModifiéFonctionneStocker les audits natifs trop volumineux dans des fichiers compagnons audit.v2 ordonnés…#817
Stockage des audits natifs trop volumineux dans des fichiers compagnons audit.v2 ordonnés, au lieu d'abandonner la disponibilité de l'audit.
#817 (ticket sur GitHub)ModifiéFonctionneDiffuser la normalisation des audits natifs dans leur contexte, sans liste intermédiaire#817
d'enregistrements.
#817 (ticket sur GitHub)CorrigéFonctionneConserver les cartes de pages d'audit natif et les métriques dérivées par page dans un#817
contexte SQLite temporaire…
Conservation des cartes de pages d'audit natif et des métriques dérivées par page dans un magasin de contexte SQLite temporaire.
#817 (ticket sur GitHub)CorrigéFonctionneConserver les constats natifs sur disque jusqu'à ce que l'agrégation ordonnée attribue#817
leurs identifiants de rapport.
#817 (ticket sur GitHub)CorrigéFonctionneConserver les groupes natifs de doublons et de contenu dans un stockage SQLite ordonné#817
pendant l'analyse.
#817 (ticket sur GitHub)ModifiéFonctionneBorner les recherches de pages répétées, regrouper les backlinks des constats et résoudre#817
les profondeurs de découverte native…
Bornage des recherches de pages répétées, regroupement des backlinks des constats et résolution des profondeurs de découverte native, sans reconstruire chaque route ni charger chaque nœud en Python. Les routes longues conservent leur profondeur exacte et leurs extrémités tronquées explicites.
#817 (ticket sur GitHub)ModifiéFonctionneDiffuser les entrées de pages natives conservées et les recherches d'inlinks avant la#817
construction du contexte.
Les relations de corpus et hreflang non vides sont dérivées via des index adossés au disque, les collections de preuves complètes sont conservées dans audit.v2, et les échantillons bornés de constats de relation sont étiquetés avec leurs nombres exacts d'occurrences.
#817 (ticket sur GitHub)ModifiéFonctionneBorner le cache de pages par octets Python décodés estimés, en plus du nombre d'entrées…#817
Le cache de pages est borné par les octets Python décodés estimés, en plus du nombre d'entrées. Les pages surdimensionnées non mises en cache restent lisibles, et les mutations invalident leur ancien poids. Les rangs de taille HTML sont conservés sous forme de paires scalaires stables URL et taille, au lieu de garder chaque modèle de page complet. Le JSON sérialisé des pages n'est plus conservé dans les callbacks de mutation.
#817 (ticket sur GitHub)ModifiéFonctionneNe conserver que les faits scalaires (URL, statut, indexabilité, canonical, redirection)#817
pendant la vérification…
Seuls les faits scalaires d'URL, de statut, d'indexabilité, de canonical et de redirection sont conservés pendant la vérification des paires avec et sans slash final. Les connexions SQLite des adaptateurs durables et des jobs distants sont fermées explicitement.
#817 (ticket sur GitHub)ModifiéFonctionneDiffuser les compteurs de couverture des corps indisponibles et du charset, conserver#817
l'état scalaire des pages de confiance…
Les compteurs de couverture des corps indisponibles et du charset sont diffusés. L'état scalaire de sélection des pages de confiance est conservé au lieu des populations de pages candidates, et les correspondances DOM sur le HTML stocké sont traitées en deux passes sur l'index de fichiers fixe. L'ordre des représentants des variantes normalisées, les exemples d'URL existants, la couverture complète des sources correspondantes et chaque constat sont préservés.
#817 (ticket sur GitHub)ModifiéFonctionneValider ligne par ligne le JSONL des points de contrôle de crawl hérités avant ajout, en#817
refusant les preuves…
Le JSONL des points de contrôle de crawl hérités est validé ligne par ligne avant ajout : les preuves manquantes, corrompues ou non terminées sont refusées sans être remplacées. Les anciens points de contrôle à formulaires inline sont comparés à leur annexe, sans allouer une seconde liste de formulaires.
#817 (ticket sur GitHub)AjoutéFonctionneAjouter une reprise de benchmark JavaScript avec provenance vérifiée entre les paires#817
terminées…
Ajout d'une reprise de benchmark JavaScript avec provenance vérifiée entre les paires terminées, en préservant les reçus antérieurs et les budgets disque. Les cas interrompus restent des bloquants explicites. Un cas de benchmark n'est marqué réussi qu'après une requête ou réanalyse réussie et des preuves CSV de pages complètes.
#817 (ticket sur GitHub)ModifiéFonctionneConserver les listes d'URL explicites avec le writer SQLite natif, en préservant l'ordre#817
d'entrée exact…
Les listes d'URL explicites sont conservées avec le writer SQLite natif, en respectant l'ordre d'entrée exact, plusieurs hôtes et la sémantique sans découverte. L'entrée par fichier d'échange est diffusée, les budgets de requêtes et de temps sont préservés lors de la reprise de collecte, les réponses bornées et les preuves de sauts sont conservées, et les contrôles graphe et formulaires restent explicitement indisponibles. Les grandes listes de répertoires utilisent le même spool, avec un pont JSON d'audit séparément borné.
#817 (ticket sur GitHub)AjoutéFonctionneAjouter un fichier compagnon SQLite audit.v2 versionné pour les collections d'audit#816
ordonnées au-delà du plafond JSON…
Ajout d'un fichier compagnon SQLite audit.v2 versionné pour les collections d'audit ordonnées complètes, au-delà du plafond de taille JSON inline, avec liaison à la provenance du scan et validation d'intégrité de bout en bout.
#816 (ticket sur GitHub)ModifiéFonctionneDiffuser les rapports audit.v2 en JSON, CSV, XLSX, Markdown et DOCX, comparer les#816
constats de gravité élevée…
Les rapports audit.v2 sont diffusés en JSON, CSV, XLSX, Markdown et DOCX. Les populations de constats de gravité élevée sont comparées, et les opérations snapshot et prune restent appariées au fichier compagnon, tout en préservant le schéma scan.v1.
#816 (ticket sur GitHub)ModifiéFonctionneConserver une seule fois les grandes appartenances ordonnées aux groupes dans audit.v2#816
révision 3 et le registre révision 5…
Les grandes appartenances ordonnées aux groupes sont conservées une seule fois dans le stockage audit.v2 révision 3 et le registre révision 5, avec lecture rétrocompatible des audits, migration atomique du registre, pagination explicite des membres via CLI et MCP, et compagnons BI d'appartenance complets et liés à leur source.
#816 (ticket sur GitHub)ModifiéFonctionnePréserver le digest audit.v2 source lors de la réanalyse hors ligne ; persister la portée#816
incomplète du registre…
Le digest audit.v2 source est préservé lors de la réanalyse hors ligne. La portée incomplète des groupes du registre est persistée, et les ressources d'ingestion sont fermées en cas d'échec de validation.
#816 (ticket sur GitHub)ModifiéFonctionneRéutiliser le résumé exact de capacité du corpus pendant qu'un scan natif sans#815
métadonnées n'a aucune preuve…
Réutilisation du résumé exact de capacité du corpus lorsqu'un scan natif en métadonnées seules ne contient aucune preuve de réponse, de document, de corps ou de ressource. Cela évite des lectures répétées de tout le corpus à chaque validation de page clairsemée, sans modifier le stockage du scan.
#815 (ticket sur GitHub)AjoutéFonctionneAjouter un profileur de capacité des scans natifs, hors ligne et sur option…#815
Ajoute, sur option et hors ligne, un profileur de capacité des scans natifs : liens, HTML conservé et DOM rendu sont dimensionnés indépendamment, avec des budgets de ressources explicites et un exercice de perte de processus et de reprise. Il présente la limite existante de 50 000 URL à l'admission et la finalisation incomplète comme des preuves bloquées, et non comme une capacité.
#815 (ticket sur GitHub)ModifiéFonctionneNommer l'annulation d'une requête navigateur épinglée
render_cancelledet fermer…#811Nomme l'annulation d'une requête navigateur épinglée
#811 (ticket sur GitHub)render_cancelledet ferme le contexte distant isolé ainsi que le navigateur en cas d'annulation ou de déconnexion, sans repli sur le local.ModifiéFonctionneAutoriser une configuration de projet distante de confiance à fournir des références#810
d'identifiants…
Permet à une configuration de projet distante de confiance de fournir des références d'identifiants d'environnement liées à l'hôte, avec une provenance conservée et masquée, et un échec avant toute collecte si une référence est indisponible.
#810 (ticket sur GitHub)ModifiéFonctionneAutoriser un transport Playwright distant, géré par le service et vérifié en version,#809
pour un projet en file…
Autorise, pour un projet en file d'attente, un transport Playwright distant géré par le service et dont la version est contrôlée. L'adresse du point d'accès reste hors des soumissions, et les requêtes navigateur épinglées et soumises à la politique restent en vigueur.
#809 (ticket sur GitHub)AjoutéFonctionneExposer les chemins observés d'un scan enregistré, les liens entrants inverses et le#807
contexte des occurrences…
Expose les chemins observés d'un scan enregistré, les liens entrants inverses et le contexte des occurrences par une requête CLI/MCP partagée, bornée et en lecture seule, avec une identité de scan explicite et des résultats d'erreur.
#807 (ticket sur GitHub)AjoutéFonctionneAjouter un positionnement des liens par occurrence, borné et en lecture seule, et le#806
contexte du titre le plus proche…
Ajoute un positionnement des liens par occurrence, borné et en lecture seule, ainsi que le contexte du titre le plus proche, à partir du HTML statique conservé et du DOM rendu. Les identifiants de liens en double sont préservés, et les données indisponibles ou partielles sont signalées explicitement.
#806 (ticket sur GitHub)AjoutéFonctionneNouvelle commande
semantic-inputs(seo_semantic_inputsvia MCP) qui construit le#801manifeste des entrées…
Nouvelle commande
#801 (ticket sur GitHub)semantic-inputs(seo_semantic_inputsvia MCP) qui construit le manifeste reproductible des entrées normalisées utilisé par le travail de similarité sémantique. Pour chaque document, elle enregistre le hash du corps conservé, le hash exact de l'entrée décodée, le hash de la sortie normalisée, la stratégie de zone de contenu et les indices de langue (le<html lang>de la page et la part des scripts de lettres), mais jamais le texte normalisé lui-même.--scandiffuse hors ligne les corps complets conservés selon la configuration de zone de contenu enregistrée pour le crawl, sans nouvelle requête. Un corps manquant ou partiel reste une omission explicite et ne donne jamais un résultat vide propre (#801).ModifiéFonctionneValider le nombre de pages, la taille, le texte extractible du PDF d'audit technique et#800
la conservation du modèle…
Valide le nombre de pages, la taille en octets, le texte extractible et la conservation des enregistrements du modèle source du PDF d'audit technique avant publication d'un rapport généré.
#800 (ticket sur GitHub)AjoutéFonctionneAjouter une sortie PDF Chromium bilingue hors ligne aux chemins CLI et MCP partagés de#799
génération de rapports…
Ajoute une sortie PDF Chromium bilingue, hors ligne, aux chemins CLI et MCP partagés de génération de rapports. Le fichier temporaire est validé et nettoyé avant son remplacement atomique.
#799 (ticket sur GitHub)AjoutéFonctionneAjouter des mises en page PDF d'audit technique autonomes, en anglais et en russe, avec#798
des jetons de marque neutres…
Ajoute des mises en page PDF d'audit technique autonomes, en anglais et en russe, avec des jetons de marque neutres, des graphiques de gravité et de couverture, des états explicites de run incomplet et un enchaînement de pages pour les contenus longs.
#798 (ticket sur GitHub)CorrigéFonctionneSéparer la mise en page PDF de la construction du modèle d'audit et de l'intégration#798
CLI/MCP.
#798 (ticket sur GitHub)AjoutéFonctionneAjouter un modèle de contenu PDF d'audit technique versionné et hors ligne, qui préserve#797
les enregistrements source…
Ajoute un modèle de contenu PDF d'audit technique versionné et hors ligne, qui préserve les enregistrements source, les compteurs exacts source et projetés, le périmètre et les états de couverture pour les deux contrats d'audit pris en charge.
#797 (ticket sur GitHub)ModifiéFonctionnePersister les versions enregistrées des scénarios de workflow, le contexte du site et des#796
concurrents, les preuves par étape…
Persiste les versions enregistrées des scénarios de workflow, le contexte du site et des concurrents, les preuves par étape et les points de validation. Une exécution interrompue peut être reprise par une session locale ultérieure, mais seulement si ses dépendances actuelles restent valides.
#796 (ticket sur GitHub)ModifiéFonctionneExiger une liste de travail du contrôleur pour lancer un workflow et conserver les#796
accusés de tri des notes de spécialiste…
Exige une liste de travail du contrôleur pour lancer un workflow, et conserve les accusés de tri explicites des notes de spécialiste pour les résultats tâche, objectif, concurrent, bloqué ou rejeté.
#796 (ticket sur GitHub)ModifiéFonctionneExiger que les points de contrôle réussis du workflow vérifient les octets des artefacts#796
conservés liés au projet…
Exige que les points de contrôle réussis du workflow vérifient les octets des artefacts conservés, liés au projet, par rapport à leurs reçus SHA-256. Des preuves manquantes ou modifiées rendent une exécution antérieure obsolète, sans réécrire l'historique. Les budgets de vérification ne certifient jamais silencieusement une preuve et ne suppriment jamais son reçu.
#796 (ticket sur GitHub)ModifiéFonctionneAutoriser des profils de politique de hachage des preuves explicites et bornés, tout en#796
normalisant les anciennes politiques.
L'observation passive du workflow vérifie les métadonnées de vérification conservées sans recalculer le hash des artefacts. Les reçus modifiés ou non vérifiés restent explicites.
#796 (ticket sur GitHub)AjoutéFonctionneAjouter une commande CLI
project progressbornée et paginée, et l'outil MCP#795seo_project_progress…Ajoute une commande CLI
#795 (ticket sur GitHub)project progressbornée et paginée, ainsi que l'outil MCPseo_project_progress, avec le nombre d'éléments par état de checklist et les prochaines actions réalisables. La vue réutilise les résultats de couverture scopés et n'affiche un pourcentage d'achèvement des tâches d'audit, clairement libellé, que si chaque site inclus dispose d'un plan convenu et d'un dénominateur de tâches mesuré et non vide. La couverture de la population d'URL reste séparée.ModifiéFonctionneLes checklists de projet peuvent désormais enregistrer le périmètre d'audit convenu et#794
afficher des ratios de couverture honnêtes (#794).
#794 (ticket sur GitHub)project-checklist-init/seo_project_checklist_initacceptent unplanfacultatif : un relecteur, unepopulationd'URL déclarée commecomplete_set, unsamplenommé ouunknown, avec sasource, sasizeou sesurlsénumérées, et des populationstemplatesfacultatives par gabarit, ainsi qu'un accordtasksfacultatif (all_agreed, ou uneselectionsourcée d'identifiants d'éléments de checklist existants). Le plan est lié à l'identité du site du projet et estampillé avec la révision d'écriture. Enregistrer un plan fait passer la checklist au formatseohead.coverage.v3et ajoute une entrée à l'historique de plans conservé. Réenregistrer un accord identique est une opération idempotente sans effet ; un accord modifié marque les preuves antérieures comme obsolètes au lieu de les réévaluer avec un dénominateur changé. Les documents v3 à plan unique migrent à la lecture. La lecture, la réconciliation ou l'application de priorités n'améliore jamais une population et n'en invente jamais une, et les documents v1 et v2 continuent à se charger sans changement. Les éléments hors sélection sont signalésnot_agreed, visibles en dehors de tous les dénominateurs.ModifiéFonctionneLa complétion de checklist suit désormais le périmètre mesuré : un élément automatique#794
limité au site…
La complétion de checklist suit désormais le périmètre mesuré. Un élément automatique limité au site, appuyé sur une mesure partielle (scan partiel), reste inachevé au lieu d'être compté comme terminé, tandis qu'un élément limité à des URL d'échantillon explicites se termine toujours lorsque l'artefact couvre exactement ce périmètre. L'indicateur
#794 (ticket sur GitHub)completede premier niveau signifie désormais que l'audit scopé est terminé : chaque tâche convenue applicable est faite, aucune exclusion n'est en attente et la population d'URL convenue est entièrement couverte par des mesures vérifiées. Ainsi, un périmètre inconnu ou non mesuré ne peut jamais signaler un audit terminé.ModifiéFonctionneLe statut de checklist renvoie désormais
coverage: cinq ratios distincts —#794audit_tasks,checks…Le statut de checklist renvoie désormais
#794 (ticket sur GitHub)coverage: cinq ratios distincts (audit_tasks,checksavecmeasured_urls,manual_review,deliverable_revieweturl_population), chacun avec sa proprebasis, sonnumerator, sondenominatoret sonstate. Un nombre d'URL n'est donc jamais mélangé à un compte de contrôles ou de tâches. Un dénominateur non justifiable restenullavecstate: "unknown"et une raison, au lieu de devenir 0/0 ou 100 %. Les mesures qui ne peuvent pas être vérifiées par rapport à la population convenue maintiennent l'axe URL enpartialet ne comptent que les URL éligibles vérifiées. L'appartenance aux URL est vérifiée contre l'énumération convenue : une population déclarée uniquement parsizene compte aucune URL mesurée, et le numérateur ne peut jamais dépasser son dénominateur. Les populations par gabarit sont validées comme sous-populations convenues de la population du site lors de l'enregistrement ou de la lecture d'un plan : les URL énumérées d'un gabarit doivent appartenir à l'ensemble de sites énuméré, et l'appartenance déclarée à un gabarit ne peut pas dépasser la taille convenue du site. Un plan incohérent est donc refusé, et non tronqué ni compté au-delà du dénominateur. Les preuves de contrôle enregistrées portent désormais l'énumération des URL mesurées, de sorte que le numérateur d'URL est un ensemble vérifiable et non un nombre de lignes. L'agrégation multi-sites additionne les axes par site et passe àunknowndès qu'un site ne peut pas justifier son dénominateur.ModifiéFonctionneUn enregistrement
not_applicabledevient une exclusion examinée : il exige une raison,#794un relecteur…
Un enregistrement
#794 (ticket sur GitHub)not_applicableest désormais une exclusion examinée : il exige une raison, un relecteur et une base de preuve inspectable, soit unartifactrelatif au projet avec digest, soit une référenceevidenceexplicite. Les exclusions examinées conservent leur identifiant stable et leur historique, ne sortent des dénominateurs de tâches que par la décision enregistrée et sont listées à part sousexclusions. Un élément désactivé, un enregistrement d'exclusion périmé ou un enregistrementnot_applicablehérité, écrit avant la règle de base de preuve, reste dans le dénominateur en tant quepending_exclusionjusqu'à un nouvel examen. Ainsi, la désactivation, le retrait du catalogue ou le reclassement ne peuvent jamais réduire silencieusement les comptes convenus. Une exclusion examinée et récente sur un élément retiré du catalogue se résout normalement, au lieu de rester en attente indéfiniment.AjoutéFonctionneAjouter le prompt
workflow/full-audit-v1et le scénariofull-auditvia le catalogue#793existant…
Ajoute le prompt adressable
#793 (ticket sur GitHub)workflow/full-audit-v1et le scénariofull-auditvia les routes de catalogue existantes des compétences et des scénarios, en CLI comme en MCP. Le workflow réutilise les chemins de collecte et de méthode déjà livrés, conditionne la vérification et le reporting aux preuves réelles, et sépare la préparation du projet de la complétion de l'audit complet.ModifiéFonctionneLier les décisions de remédiation à des artefacts de vérification immuables et aux hash#792
avant/après exacts…
Lie les décisions de remédiation à des artefacts de vérification immuables et aux hash exacts des preuves avant et après, y compris pour les bases de référence audit.v2 diffusées en flux.
#792 (ticket sur GitHub)ModifiéFonctionnePrésenter les résultats de remédiation conservés avec les états de couverture#791
« représentée » et « périmètre complet »…
Présente les résultats de remédiation conservés avec les états de couverture « représentée » et « périmètre complet », des dénominateurs par tâche et des pages de cas bornées pour les grands registres.
#791 (ticket sur GitHub)AjoutéFonctionneAjouter des contrôles de remédiation bornés, indexés par ledger, qui réutilisent le#790
vérificateur protégé…
Ajoute des contrôles de remédiation bornés, indexés par ledger, qui réutilisent le vérificateur protégé, diffusent les sélections audit.v2 et refusent toute dérive de périmètre.
#790 (ticket sur GitHub)ModifiéFonctionneLier la vérification de remédiation à l'identité d'audit conservée et à la politique#790
d'origine…
Lie la vérification de remédiation à l'identité d'audit conservée et à la politique d'origine qui affecte les résultats. Un UUID de scan réutilisé ne suffit plus à résoudre un ancien cas.
#790 (ticket sur GitHub)AjoutéFonctionneAjout du ledger
ledger.v1de remédiation (seohead.storage.ledger, identifiant#788d'application
SEOL)…Ajout du ledger de remédiation
#788 (ticket sur GitHub)ledger.v1(seohead.storage.ledger, identifiant d'applicationSEOL) : un artefact SQLite distinct, lié au projet, qui enregistre les constats, l'identité de chaque occurrence, l'appartenance aux URL concernées et un historique d'observations en ajout seul, à partir d'auditsscan.v1etscan.v2validés, sans modifier les scans sources. L'ingestion lie chaque source par UUID de scan, plus révision des preuves, digest d'audit et identité du producteur et de la configuration. Les rejeux exacts sont idempotents, les octets contradictoires sous une même révision sont refusés de façon atomique, et la couverturecappedouaggregate_onlyainsi que les preuvesunavailablesont conservées comme données, sans être traitées silencieusement comme résolues. Inclut une migration transactionnelle 0→1, une validation complète du schéma et de l'intégrité à l'ouverture, etdocs/LEDGER.md(#788).AjoutéFonctionneAjouter un profil jetable de reverse proxy TLS en boucle locale qui vérifie l'isolation#787
des artefacts authentifiés…
Ajoute un profil jetable de reverse proxy TLS en boucle locale (loopback) qui vérifie l'isolation des artefacts authentifiés, la sauvegarde et la restauration, l'expiration, et le retour arrière atomique lors d'une mise à niveau, sans déployer de service.
#787 (ticket sur GitHub)ModifiéFonctionneDocumenter les tâches CLI SSH ponctuelles, les enregistrements d'artefacts et#787
d'incidents, la récupération synthétique…
Documente les tâches CLI SSH ponctuelles, les enregistrements d'artefacts et d'incidents, la récupération synthétique, ainsi que la frontière distincte et future du service (santé, proxy, TLS et pare-feu).
#787 (ticket sur GitHub)AjoutéFonctionneAjouter le profil opérateur du service distant auto-hébergé pour les liaisons privées,#787
les preuves de santé authentifiées…
Ajoute le profil opérateur du service distant auto-hébergé : liaisons privées, preuves de santé authentifiées, références de secrets par fichier, enregistrements d'incidents, restauration cohérente avec SQLite, retour arrière de version et expiration explicite des artefacts terminaux.
#787 (ticket sur GitHub)AjoutéFonctionneAjouter une politique de cible distante liée au projet pour les futures tâches en file#786
d'attente.
Lorsqu'un worker la lie, elle valide les cibles et les paramètres, revérifie le DNS au moment de la distribution, épingle les requêtes HTTP et les requêtes de ressources navigateur, et applique des budgets de requêtes par origine et au total. Les proxys distants et les backends navigateur alternatifs échouent en mode fermé tant que leur sortie réseau n'est pas prouvée sûre.
#786 (ticket sur GitHub)AjoutéFonctionneAjouter une file SQLite isolée par projet et un worker de scan natif avec bail pour l'API#785
distante optionnelle…
Ajoute une file SQLite isolée par projet et un worker de scan natif avec bail, pour l'API distante authentifiée et optionnelle. Les tâches conservent les scans natifs et les rapports JSON/Markdown, prennent en charge la soumission idempotente, l'annulation, la reprise après échec au redémarrage, les téléchargements d'artefacts limités au périmètre et une rétention explicite, sans activer d'écouteur par défaut.
#785 (ticket sur GitHub)ModifiéFonctionneDéfinir un contrat optionnel, authentifié et versionné d'API de scan distant, avec des#784
paramètres de crawl natifs bornés…
Définit un contrat optionnel, authentifié et versionné d'API de scan distant, avec des paramètres de crawl natif bornés, des permissions limitées au projet, une soumission idempotente, un schéma OpenAPI et des preuves compatibles avec le statut de scan. Il exige des workers durables et séparés, ainsi qu'une politique de cibles et de sortie réseau ; les installations locales CLI/MCP ne changent pas (#784).
#784 (ticket sur GitHub)AjoutéFonctionneAjouter un runbook SSH Ubuntu épinglé et un test de cycle de vie Linux jetable qui prouve#783
l'installation…
Ajoute un runbook SSH Ubuntu épinglé et un test de cycle de vie Linux jetable, qui prouve l'installation, le rendu JavaScript, la découverte des artefacts, la mise à niveau, les preuves de version et le retour arrière, sans service distant.
#783 (ticket sur GitHub)ModifiéFonctionneNormaliser les données analytiques et de recherche importées pour les jointures de scan#781
(#781).
#781 (ticket sur GitHub)evidence-normalizelit intégralement hors ligne un export CSV/XLSX/JSON fourni ou une enveloppeseohead.provider-evidence.v1enregistrée, et produit un documentseohead.normalized-evidence.v1versionné à partir d'un manifesteseohead.evidence-mapping.v1facultatif. Il conserve la provenance par champ (enveloppe, déclarée ou inconnue) pour la source, l'identité du rapport, la période, le fuseau horaire, l'attribution, le moteur, la couverture de collecte et le lien aux URL. Une mesure0reste zéro, tandis que les lignes manquantes, les cellules nulles ou vides, les cellules de formule, les valeurs supprimées, les périodes non collectées et une collecte échouée ou ignorée restent explicitement indisponibles.evidence-joinjoint les données normalisées à une liste de scan, d'audit ou de pages selon la politique de clé stricteexternal_join.v1, avec des assouplissements explicites uniquement. Il conserve les populations appariées, présentes seulement dans le crawl, présentes seulement en externe et non clés, au grain déclaré, et signale les collisions de clés au lieu de multiplier les lignes.--compareavec une politique déclarée ajoute une décision pure compatible, incompatible ou inconnue sur la période, la frontière de fuseau horaire, l'identité, l'attribution, le moteur et le grain, et signale l'éligibilité des quadrants à zéro mesuré pour les travaux de cohorte en aval. Les sources restreintes conservent leurs lignes et identifiants dans un artefact local privé et ne renvoient que des comptes.AjoutéFonctionneAjouter une expérience hors ligne de disponibilité des fournisseurs, avec des références#780
d'identifiants masquées…
Ajoute une expérience hors ligne de disponibilité des fournisseurs, avec des références d'identifiants masquées, des schémas de routes d'opération, des métadonnées de quota et de confidentialité, et des états d'accès à la cible explicites. La seule présence d'un identifiant ne vérifie jamais une autorisation.
#780 (ticket sur GitHub)ModifiéFonctionneÉtendre
seohead watchen observateur de projet en lecture seule, avec navigation dans#775les constats conservés…
Étend
#775 (ticket sur GitHub)seohead watchen observateur de projet en lecture seule, avec navigation dans les constats conservés, détail des preuves, état des vues enregistrées et de la revue, et notifications de boîte de réception MCP explicitement limitées à un périmètre.AjoutéFonctionneAjouter la livraison autorisée de rapports synthétiques, avec des reçus d'idempotence#773
durables…
Ajoute la livraison autorisée de rapports synthétiques, avec des reçus d'idempotence durables et un comportement sûr en cas de nouvelle tentative après redémarrage.
#773 (ticket sur GitHub)ModifiéFonctionneConserver les revendications de livraison incertaines après une sortie de processus ou#773
une confirmation malformée…
Conserve les revendications de livraison incertaines après une sortie du processus ou une confirmation de document malformée, en empêchant un second envoi automatique lorsque l'acceptation ne peut pas être établie.
#773 (ticket sur GitHub)ModifiéFonctionneMesurer les aperçus de rapports à partir des preuves conservées, y compris la taille#772
rendue et le digest source…
Mesure les aperçus de rapports à partir des preuves conservées, y compris la taille rendue, le digest source, les effectifs de population, la couverture et le profil ; signale les moteurs de rendu manquants et les entrées trop volumineuses avant envoi. Aligne le budget JS du wizard approfondi sur le contrat de file partagé.
#772 (ticket sur GitHub)ModifiéFonctionneConserver les identités exactes de distribution confirmées du bot malgré les plantages de#771
file et de propriété…
Conserve les identités exactes des distributions de bot confirmées, malgré les plantages de file et de propriété, déduplique les mises à jour de confiance et isole le statut, l'annulation, la reprise et la livraison par acteur et chat à l'origine. Des relances volontaires identiques restent des tâches distinctes.
#771 (ticket sur GitHub)CorrigéFonctionneRestaurer la dernière tâche confirmée de l'acteur et du chat après le redémarrage du#771
service…
Restaure la dernière tâche confirmée de l'acteur et du chat après le redémarrage du service, en préservant le comportement d'annulation et de relance reconfirmée, sans reprendre un brouillon non confirmé.
#771 (ticket sur GitHub)AjoutéFonctionneAjouter le paquet de preuves de revue logicielle pour l'auto-hébergement, le juridique et#761
les achats…
Ajoute le paquet de preuves de revue logicielle pour l'auto-hébergement, la revue juridique et les achats (#761).
#761 (ticket sur GitHub)scripts/generate_review_pack.pygénère et vérifie un inventaire versionné des dépendances et licences (un profil par groupe d'installation, avec versions résolues et sources de licence mesurées dans l'environnement de génération ; les exigences exclues par marqueur, non satisfaites par la spécification ou non installées sont consignées et non supprimées). Il produit aussi un document de provenance au niveau de la version, qui relie les distributions construites à un tag et à la révision source du manifeste de build embarqué, ainsi qu'unSHA256SUMS.txtcouvrant l'ensemble. La génération refuse un tag qui ne correspond pas àpyproject.toml. La vérification revalide les formats et les hash enregistrés, réextrait chaque distribution pour revalider son manifeste embarqué, le nom et la version déclarés par son nom de fichier, ainsi que les champsNameetVersionde son propre*.dist-info/METADATAouPKG-INFOpar rapport àpyproject.toml, sans faire confiance à l'enregistrement. Un marqueur sentinelleSEOHEAD_REVIEW_CANARY_*prouve que les fichiers du paquet et les diagnostics ne contiennent aucun identifiant ou marqueur client glissé. La CI régénère et vérifie le paquet avec un tag jetable à chaque exécution ; le workflow de release le vérifie et l'attache à une vraie releasev*.AjoutéFonctionneAjouter
docs/SOFTWARE_REVIEW.md: la carte des points d'accès sortants et des flux de#761données…
Ajoute
#761 (ticket sur GitHub)docs/SOFTWARE_REVIEW.md: la carte des points d'accès sortants et des flux de données (ce que chaque fonction contacte et envoie, et si l'appel est optionnel), la frontière locale et distante, le stockage des identifiants et des artefacts, l'état de la télémétrie, le signalement de sécurité et l'état de maintenance, ainsi que la manière de relire les changements de release. Il renvoie vers les runbooks d'installation et de déploiement (#783, #787) au lieu de les dupliquer.AjoutéFonctionneAjouter une vérification préalable hors ligne de capacité et de somme de contrôle pour#760
Google Sheets…
Ajoute une vérification préalable hors ligne de capacité et de somme de contrôle pour Google Sheets, destinée aux paquets BI locaux complets ; elle n'effectue aucune requête Google ni aucune écriture dans le tableur.
#760 (ticket sur GitHub)AjoutéFonctionneAjouter un transport explicite de remplacement Google Sheets, à liste blanche, avec des#760
mappings de feuilles par jeu de données…
Ajoute un transport explicite de remplacement Google Sheets, limité par liste blanche, avec des correspondances de feuilles par jeu de données, une préparation et une relecture bornées, et une publication atomique unique de valeurs seules qui conserve les identifiants de feuilles existants.
#760 (ticket sur GitHub)ModifiéFonctionneJournaliser la préparation Google Sheets avant toute création d'étape distante et#760
réconcilier une réponse perdue…
Journalise la préparation Google Sheets avant toute création d'étape distante, et réconcilie une réponse de début perdue à partir des identifiants déterministes des feuilles de préparation avant toute nouvelle tentative.
#760 (ticket sur GitHub)ModifiéFonctionnePréserver les valeurs numériques et booléennes typées et les relectures Google#760
clairsemées, rejeter les cibles en double…
Préserve les valeurs numériques et booléennes typées ainsi que les relectures Google clairsemées, rejette les cibles de feuilles en double et la dérive de contrat de jeu de données, lie la reprise à la cible configurée exacte et conserve la préparation reprenable après épuisement du quota. Budgète la croissance de la grille cible avant le remplacement atomique et signale les nombres de lignes non confirmés lors des exécutions interrompues.
#760 (ticket sur GitHub)ModifiéFonctionneValider les compagnons de groupe complets et liés à la source avant toute écriture de#760
destination…
Valide les compagnons de groupe complets et liés à la source avant toute écriture dans la destination, les conserve avec les constats sélectionnés, et signale les références d'appartenance purement locales dans les reçus de destination et de classeur, sans prétendre que le compagnon a été envoyé.
#760 (ticket sur GitHub)ModifiéFonctionneRépartir les grands ensembles d'enregistrements XLSX de
scan-exporten feuilles#759numérotées déterministes…
Répartit les grands ensembles d'enregistrements XLSX de
#759 (ticket sur GitHub)scan-exporten feuilles numérotées et déterministes, avec une feuille Partitions qui conserve le nombre de lignes, au lieu d'échouer à la limite de lignes par feuille d'Excel.ModifiéFonctionneRépartir les feuilles Constats et Pages de
report-buildà la limite de lignes d'Excel…#759Répartit les feuilles Constats et Pages de
#759 (ticket sur GitHub)report-buildà la limite de lignes d'Excel et émet un.xlsx.index.jsonhaché, avec les plages exactes d'enregistrements, la population source, ainsi que les références de sélection et de couverture. Le rapport précédent est conservé en cas d'échec d'écriture détecté, et les fichiers temporaires de streaming sont nettoyés en cas d'interruption.ModifiéFonctionneRefuser les cellules XLSX de plus de 32 767 caractères avant que la bibliothèque ne les#759
tronque…
Refuse les cellules XLSX de plus de 32 767 caractères avant que la bibliothèque ne les tronque, protège les métadonnées du résumé contre l'injection de formules et rejette les lignes d'audit conservées malformées avant qu'un export incomplet puisse se déclarer réussi. Les exports CSV et JSON complets ainsi que la politique bornée de vue d'ensemble PDF restent disponibles.
#759 (ticket sur GitHub)AjoutéFonctionneAjouter une livraison CSV BI sélectionnée et bornée, avec un comptage exact des lignes#759
source, sélectionnées et omises…
Ajoute une livraison CSV BI sélectionnée et bornée, avec un comptage exact des lignes source, sélectionnées et omises, la couverture source conservée, des hash calculés en flux et un index durable des plages CSV vers feuille XLSX. Les cellules Excel trop grandes échouent avant toute troncature, et une sortie interrompue n'est jamais marquée comme complète.
#759 (ticket sur GitHub)ModifiéFonctionnePréserver les lignes de couverture des contrôles, complètes et immuables, à côté des#759
exports BI de constats…
Préserve les lignes de couverture des contrôles, complètes et immuables, à côté des exports BI de constats sélectionnés, y compris les raisons des contrôles ignorés, et permet une sélection exacte des constats par segment de périmètre de crawl conservé.
#759 (ticket sur GitHub)AjoutéFonctionneAjouter des diagnostics de crawl hors ligne pour les scans natifs enregistrés et les#758
anciens artefacts de run…
Ajoute des diagnostics de crawl hors ligne pour les scans natifs enregistrés et les anciens artefacts de run, avec des preuves de décision bornées, des explications actionnables, un accès CLI/MCP en lecture seule et une commande et un outil d'export masqué distincts, activables sur option.
#758 (ticket sur GitHub)AjoutéFonctionneAjout de vues de constats de projet versionnées, avec filtres fermés et tri tenant compte#757
de la gravité…
Ajout de vues de constats de projet versionnées, avec filtres fermés, tri tenant compte de la gravité, projection de colonnes et pagination bornée. Le tri par gravité décroissante place les constats critiques en premier ; le tri croissant va de l'avis au critique. La CLI, le MCP, les rapports et la future navigation dans le terminal peuvent réutiliser la même vue en lecture seule, sans modifier les preuves sources, le scoring, les tâches ni la couverture.
#757 (ticket sur GitHub)AjoutéFonctionneAjouter une planification de surveillance locale désactivée par défaut, avec des budgets#756
explicites…
Ajoute une planification de surveillance locale, désactivée par défaut, avec des budgets explicites pour les rafraîchissements incrémentiels et complets, un refus de chevauchement, des revendications de reprise, une provenance d'observation immuable, la conservation des résultats partiels, des alertes dédupliquées et des notifications de rétablissement. Le moniteur ne démarre ni minuterie, ni crawl, ni canal de livraison.
#756 (ticket sur GitHub)AjoutéFonctionneAjouter un adaptateur de livraison de service explicite et injecté pour les notifications#756
complètes du moniteur.
Exige une destination autorisée liée au projet et un magasin de reçus durable ; aucun transport, compte, minuterie ni destination n'est activé par défaut.
#756 (ticket sur GitHub)ModifiéFonctionneRendre les revendications de surveillance conservées, la couverture et la provenance du#756
cache sûres au redémarrage…
Rend sûrs au redémarrage les revendications de surveillance conservées, la couverture, la provenance du cache, les preuves de rétablissement dérivées et la livraison de service facultative.
#756 (ticket sur GitHub)AjoutéFonctionneAjouter un collecteur de moniteur ponctuel, explicite et borné par bail, avec des preuves#756
conservées…
Ajoute un collecteur de moniteur ponctuel, explicite et borné par bail, avec des preuves conservées tenant compte du cache et une livraison locale uniquement avec reçus.
#756 (ticket sur GitHub)AjoutéFonctionneAjout de
verify-fixes/seo_verify_fixespour revérifier des URL sélectionnées, dans#755des limites données…
Ajout de
#755 (ticket sur GitHub)verify-fixes/seo_verify_fixes: revérification bornée d’URL sélectionnées, à partir d’ID de constats enregistrés, d’une vue de vérification ou d’une liste d’URL explicite. La commande réutilise la politique de crawl enregistrée, écrit un artefact de vérification distinct sans écrasement ainsi qu’un rapport ciblé, et distingue les constats résolus, persistants, modifiés et non vérifiables. Un audit « after » existant ne peut être vérifié sans accès réseau que s’il possède un UUID de scan distinct et une date d’observation enregistrée plus récente. Une preuve non mesurée ou à l’échelle du site n’est jamais étiquetée comme corrigée.ModifiéFonctionneLes crawls natifs d’URL peuvent filtrer les routes découvertes par extension de fichier#749
avant…
Les crawls natifs d’URL peuvent filtrer les routes découvertes selon l’extension finale du nom de fichier avant les requêtes, et selon le
#749 (ticket sur GitHub)Content-Typeréel de la réponse avant l’analyse ou la conservation du corps. Les deux étapes de filtrage journalisent leurs décisions tout en conservant les preuves de liens, de statuts, d’en-têtes et de redirections ; les métadonnées HTML filtrées restent explicitement non mesurées. Les ressources de script et de feuille de style conservent leur sélection indépendante et leurs propres budgets d’octets.ModifiéFonctionneSi le corps HTML est indisponible, les constats dérivés conservent une couverture#749
« ignorée » explicite…
Si le corps HTML est indisponible, les constats dérivés du corps conservent désormais une couverture explicitement ignorée, au lieu de traiter les valeurs par défaut de l’analyseur comme propres ou comme métadonnées manquantes observées.
#749 (ticket sur GitHub)AjoutéFonctionneAjout d’exclusions auditables de motifs d’URL après analyse, pour les constats.#748
Les constats supprimés restent dans le JSON d’audit et dans les exports de build de rapport, avec la règle correspondante et la raison. Les totaux des constats actifs, le score et le backlog de tâches reflètent la politique configurée. Les artefacts de scan natifs conservent cette politique pour une nouvelle analyse hors ligne ; le nombre de règles et la longueur des champs sont bornés.
#748 (ticket sur GitHub)AjoutéFonctionneAjout de
scan-export(seo_scan_export) : un export hors ligne et en lecture seule des#747données de scan, sous le contrat versionné…
Ajout de
#747 (ticket sur GitHub)scan-export(seo_scan_export) : un export hors ligne et en lecture seule des données de scan, selon le contrat versionnéscan_export.v1. La commande lit un artefact SQLitescan.v1validé ou unaudit.jsonde SF Analyzer, et écrit du CSV (un fichier par type d’enregistrement, plus un manifeste), du XLSX (classeur en écriture seule), une enveloppe JSON en flux, ou un document XML documenté. Chaque sortie contient la provenance, le nombre de lignes exportées, les statistiques d’exécution et une couverture explicite, y compris les contrôles exécutés, ignorés, désactivés ou restés non comptabilisés, que la sélection de champs ne peut pas supprimer. Les options--recordset--fieldsassurent une projection validée sur les enregistrements de pages, de liens et de constats ; les types d’enregistrement ou les noms de champs inconnus sont rejetés avant la création du moindre fichier de sortie. Les valeurs absentes restent explicites (nullen JSON,state="absent"en XML,\Nen CSV/XLSX) au lieu de devenir des zéros fabriqués. La sortie XML est une sérialisation générée, avec un échappement UTF-8 correct ; elle n’analyse jamais de XML, de DTD, de feuilles de style ni d’entités fournis par l’appelant. Le CSV et le XLSX conservent la protection existante contre l’injection de formules ; le XLSX refuse les dépassements de lignes, de colonnes et de texte de cellule au lieu de tronquer, et toute erreur de sérialisation ne laisse aucun artefact partiel plausible.ModifiéFonctionne
crawl-sitegagne une vérification bornée et optionnelle des destinations externes#746enregistrées :
#746 (ticket sur GitHub)discovery.external.crawllance une phase distincte après la fermeture de la file de crawl interne, et récupère une fois chaque cible de lien sortant distincte, sous ses propres budgetsexternal_checks.max_targets,max_hosts,max_requests,max_depthetmax_redirects. Jamais de parcours récursif hors de l’hôte. Les résultats (récupéré, échoué, bloqué, ignoré avec la règle qui a refusé) sont ajoutés àexternal_checks.jsonldans le répertoire de l’exécution, conservent la provenance de la page source, persistent après reprise sans rouvrir les budgets épuisés, et publient leur propre couverture dansdiscovery.external_checks, distincte de l’exhaustivité du crawl interne. Nécessitediscovery.external.store; refusé nommément sur la route SQLite native (--scan-out) tant que ce collecteur n’intègre pas la phase.AjoutéFonctionneAjout d’une politique explicite et validée de proxy HTTP sortant pour le crawl natif, sur#745
toutes les requêtes…
Ajout d’une politique explicite et validée de proxy HTTP sortant pour le crawl natif, couvrant les requêtes de pages, de sitemaps, de ressources et du navigateur épinglé, avec une provenance de route masquée.
#745 (ticket sur GitHub)ModifiéFonctionneL’étape de rendu du crawler natif expose désormais son navigateur par la configuration,#744
au lieu de figer Chromium :
#744 (ticket sur GitHub)rendering.browser.enginechoisitchromium(toujours par défaut),firefoxouwebkit, via le même chemin Playwright protégé. Un nom inconnu échoue à la validation des réglages, et non au lancement. Un moteur demandé dont le binaire n’est pas installé renvoie une erreur de dépendance qui indique la correction (playwright install <engine>), au lieu de revenir silencieusement à Chromium. La limite documentée de Firefox est appliquée honnêtement : il rejetterendering.browser.mobile_emulation(is_mobilen’y est pas pris en charge), tandis querendering.browser.touch_emulation(has_touch), indépendant, atteint toujours le contexte. Les sondes et les rendus complets partagent une même configuration effective : un motif n’est donc jamais escaladé par un navigateur différent de celui qui a produit ses preuves.ModifiéFonctionne
rendering.browser.viewport_widthetrendering.browser.viewport_heightajoutent des#744dimensions de viewport libres aux préréglages existants
#744 (ticket sur GitHub)desktopetmobile, qui continuent de définir le profil comme avant. La paire est tout ou rien et bornée à 1..16384 px. Un couple personnalisé remplace uniquement les dimensions du préréglage – il n’active ni l’émulation mobile ni l’émulation tactile, qui restent des indicateurs explicites que l’opérateur doit déclarer.ModifiéFonctionne
rendering.browser.page_concurrencyborne le nombre de récupérations navigateur en cours#744pendant l’escalade…
#744 (ticket sur GitHub)rendering.browser.page_concurrencyborne le nombre de récupérations navigateur simultanées pendant l’escalade, indépendamment despeed.concurrency, qui continue de gérer la politesse du crawl HTTP. La valeur par défaut, 1, conserve le comportement séquentiel et ne crée aucun pool. Au-delà de 1, les sondes et les rendus tournent sur un pool de workers borné, tandis que chaque verdict, chaque validation et chaque écriture SQLite restent sur le thread orchestrateur, dans l’ordre de soumission. Le délai de rendu, le budget d’URL et les politiques par échantillon ou complètes tiennent sous concurrence ; un lot arrêté attend les pages déjà démarrées et n’en planifie aucune nouvelle ; les workers interrompus ou en échec ne laissent aucun thread, page, contexte ni navigateur actif derrière l’appel.ModifiéFonctionneLe moteur choisi, sa version réellement rapportée, le viewport effectif et la concurrence#744
de pages configurée sont enregistrés dans le manifeste et l’empreinte du crawl, ainsi que dans la provenance de rendu de chaque document rendu, y compris les tentatives échouées, qui consignent ce qui a été demandé sans inventer de rendu réussi. Les deux chemins d’escalade respectent ces réglages : le crawl de répertoire et le scan SQLite natif, où les candidats de liens rendus entrent dans la file via l’admission dédupliquée existante, une fois
#744 (ticket sur GitHub)rendering.rendered_links.crawlactivé.AjoutéFonctionneAjout de contrôles optionnels de politique canonical pour les motifs d’URL de pagination#727
et de filtres configurés, dans les pipelines d’audit de l’export SF et du crawl natif. Les règles de pagination acceptent comme cible la page elle-même, la première page ou une page d’atterrissage ; les règles de filtres acceptent la page elle-même ou une page d’atterrissage. Les politiques absentes et les cibles absentes des preuves de crawl restent non mesurées, et les contrôles de statut de réponse des destinations restent distincts.
#727 (ticket sur GitHub)AjoutéFonctionneAjout d’archives GSC Search Analytics reprenables dans l’historique local partagé des#718
fournisseurs (SQLite)…
Ajout d’archives reprenables des données Search Analytics de GSC dans le stockage SQLite local partagé de l’historique des fournisseurs, accessible de façon cohérente via
#718 (ticket sur GitHub)gsc-archiveetseo_gsc_archive. Le statut hors ligne, la préparation explicite, la collecte bornée et les instantanés vérifiés reposent sur un même noyau.ModifiéFonctionneConservation de la provenance fournisseur/moteur et des métriques nullables, mise en file#718
des seules plages de disponibilité non couvertes, et persistance des attentes de quota et des nouvelles tentatives transitoires bornées.
#718 (ticket sur GitHub)AjoutéFonctionneAjout d’un historique SQLite local et versionné pour GA4, Metrika, les requêtes et pages#716
web GSC, et Yandex…
Ajout d’un historique SQLite local et versionné, unique, pour les données quotidiennes de GA4, Metrika, des requêtes et pages web de GSC, et de Yandex Webmaster, avec remplacement atomique par journée et couverture explicite : demandée, vide, partielle, en échec ou en retard.
#716 (ticket sur GitHub)AjoutéFonctionneSynchronisation déterministe et bornée, statut hors ligne et export JSON/CSV via le CLI#716
et le MCP local partagés…
Synchronisation déterministe et bornée, statut hors ligne et export JSON/CSV exposés via le CLI et le MCP local partagés, avec l’additivité des métriques et des métadonnées de fuseau horaire prudentes. Aucun identifiant ni donnée de fournisseur n’est stocké dans le dépôt.
#716 (ticket sur GitHub)ModifiéFonctionneLe mode A de SF vérifie désormais les capacités d’export demandées par le CLI installé et#715
exige un jeu complet et récent…
Le mode A de SF vérifie désormais les capacités d’export demandées par le CLI installé et exige un jeu complet d’exports récents avant toute analyse. La provenance du profil complet enregistre le manifeste d’export vérifié et la relecture de la vitesse de crawl ; une limite de débit explicite échoue avant le crawl lorsque sa configuration de base est absente ou invalide.
#715 (ticket sur GitHub)ModifiéFonctionne
metrika-report(seo_metrika_report) etMetrikaClient.report(): un refus « Query is#714too complicated » de l’API Reporting est désormais relancé par tranches de mois civils, avec une attente de 5 à 15 s. Une tranche qui refuse encore est renvoyée avec un échantillonnage
#714 (ticket sur GitHub)accuracy=0.1. Seules les métriques prouvées additives sur des périodes disjointes (ym:s:visits,ym:s:pageviews) sont fusionnées. Une requête portant sur des visiteurs uniques, des pourcentages, des moyennes ou des métriques non vérifiables est refusée avant la première requête de tranche, plutôt que de renvoyer une valeur sommée et fausse. Les tranches sont fusionnées au fil de leur arrivée sous un plafond global de lignes, qui borne aussi les téléchargements : une fois ce plafond atteint, les partitions restantes ne sont jamais demandées et leurs intervalles de dates sont nommés danssplit.unfetched. Une tranche interrompue en cours de route y indique son propre intervalle avecpartial. Lestotal_rowsettotalsde la période entière restent inconnus lorsque les périodes non interrogées les rendent impossibles à connaître, et la fenêtre renvoyée est marquée partielle. Les réponses fusionnées réappliquent lesortde l’appelant (dimensions et métriques, ou le tri par défaut de l’API : première métrique décroissante), et la fenêtreoffset/limitest prise dans l’ensemble fusionné global. Les champs d’échantillonnage reflètent ce que l’API a réellement rapporté (sampled, piresample_share) ; une tranche qui ne les renvoie pas laisse la valeur de la période entière inconnue, soitnulldans la réponse CLI/MCP, jamais unfalseinventé.contains_sensitive_dataest la disjonction (OU) des tranches,min/maxsont recalculés sur les lignes fusionnées, et les cellules ou totaux de métriques absents ou mal formés marquent le corpsincompleteau lieu d’être lus comme zéro. Le blocsplitconserve les périodes demandées et résolues, le fuseau horaire utilisé, ainsi que la précision, les tentatives, les lignes écartées et les métadonnées d’échantillonnage de chaque tranche. Une partition en échec nomme les intervalles terminés et les précisions épuisées. Les dates relatives (today,NdaysAgo) se résolvent dans letimezonede la requête ou dans le fuseau du compteur (time_zone_name, sinon son décalage en minutes), jamais dans la date de l’hôte. Une période qui ne peut pas être convertie en dates (un raccourci commemonth) est tout de même relancée et échantillonnée avant que l’erreur ne soit remontée. Unaccuracyexplicite est respecté sous toutes les formes documentées (low,medium,high,full, ou une part numérique comme"1") : tout ce qui dépasse l’échantillon de dernier recours conserve le repliaccuracy=0.1, tandis qu’un réglage déjà plus bas (lowou une part égale ou inférieure) est préservé et n’est pas rééchantillonné à la hausse. Chaque requête tentée, y compris l’appel global refusé, est inscrite dans le journal des dépenses.ModifiéFonctionneYandex Webmaster : l’opération
indexinglit désormais le vrai endpoint v4 et non plus#713un chemin inexistant…
Yandex Webmaster : l’opération
#713 (ticket sur GitHub)indexinglit désormais le vrai endpoint v4/indexing/samplesau lieu de l’inexistant/search-urls. La nouvelle opérationbroken_links_sampleslit/links/internal/broken/samples, et la pagination respecte la taille de page documentée de chaque endpoint (500 pour les requêtes, 100 pour les listes d’échantillons) au lieu d’envoyerlimit=500partout (#713).ModifiéFonctionneLa collecte paginée de Yandex Webmaster valide désormais le
countdocumenté de chaque#713page et la forme de sa liste, y compris un
#713 (ticket sur GitHub)countinférieur au nombre de lignes renvoyées. Elle demande au plus le reliquat du budget de lignes à chaque page, afin qu’une coupuremax_rowsne puisse jamais dépasser et produire un fauxcomplete. Pour une page unique non paginée, elle exposereturnedettruncatedsous forme de tranche bornée, signalepartialdès qu’il reste des lignes non lues, et fait échouer les pages mal formées au lieu d’annoncer un résultat vide complet (#713).ModifiéFonctionneLe render-check utilise désormais la confiance de la plateforme, plus une autorité de#709
certification explicitement choisie…
Le render-check utilise désormais la confiance de la plateforme, complétée par une autorité de certification (CA) explicitement choisie, rejoue les sous-requêtes POST épinglées et signale les échecs de vérification de certificat sans détail de transport.
#709 (ticket sur GitHub)ModifiéFonctionneReconnaissance de l’échec de vérification TLS du transport proxy épinglé, et vérification#709
de la confiance explicite envers une CA de fixture…
Reconnaissance de l’échec de vérification TLS du transport proxy épinglé, et vérification, via un intercepteur CONNECT synthétique contrôlé par le projet, de la confiance explicite envers la CA de fixture, du rejeu POST, des plafonds de réponse et du rejet des cibles privées, sans modifier la confiance de la plateforme.
#709 (ticket sur GitHub)
3 octobre 2026
5 ajouté8 modifié1 corrigéAjoutéFonctionneAjout de
crawl-importpour des bundles CSV de crawl tiers explicitement mappés, en#819conservant l’identité source…
Ajout de
#819 (ticket sur GitHub)crawl-importpour des bundles CSV de crawl tiers explicitement mappés. La commande conserve l’identité de la source étrangère, la couverture par champ, les observations en double et les données indisponibles dansthird_party_crawl.v1, sans traiter le résultat comme un scan natif ni comme un audit SF.AjoutéFonctionneAjout d’un transport Playwright distant optionnel vers le navigateur, avec vérification#808
explicite de l’endpoint et de la version, états d’échec sûrs et aucun repli sur un lancement local, tout en conservant le rendu local par défaut.
#808 (ticket sur GitHub)AjoutéFonctionneAjout de requêtes Python en lecture seule, bornées et déterministes : chemin observé le#805
plus court et recherche de liens entrants…
Ajout de requêtes Python en lecture seule, bornées et déterministes, sur les liens des scans conservés : chemin observé le plus court et recherche de liens entrants. Chaque saut porte l’identité de l’enregistrement de scan, et la couverture d’un graphe partiel est explicite.
#805 (ticket sur GitHub)ModifiéFonctionneAligner la régression de routage des workflows fournisseur sur le contrat corrigé de#779
requête de fréquence Wordstat d’Arsenkin (#720, #779).
Alignement de la régression de routage des workflows fournisseur sur le contrat corrigé de la requête de fréquence Wordstat d’Arsenkin (#720, #779).
#779 (ticket sur GitHub)AjoutéFonctionneAjout de
docs/PROVIDERS.md, une matrice générée des capacités fournisseurs et des#779workflows spécialisés (#779, épopée #753). Elle est produite à partir du registre des fournisseurs et d’un catalogue de workflows curé dans
#779 (ticket sur GitHub)seohead/core/provider_matrix.py, viascripts/generate_provider_matrix.py. Pour chaque workflow, elle indique les commandes livrées, l’authentification, le coût et le quota, la classe de confidentialité, les limites, et le repli CSV viaprovider-join. Les statuts distinguent pris en charge, partiel, non pris en charge et non vérifié ; aucun fournisseur déclaré n’est étiqueté comme vérifié en conditions réelles, les travaux explicitement non pris en charge sont listés nommément, et une première version par phases est retenue sans prétendre couvrir tous les services. Les écarts entre le document versionné et les registres sont détectés partests/test_docs_drift.pyettests/test_provider_matrix.py.CorrigéFonctionneCorrection de
keywords-exact/seo_keywords_exact, qui ne pouvait jamais renvoyer de#720résultat : la tâche visait un outil Arsenkin inexistant…
Correction de
#720 (ticket sur GitHub)keywords-exact/seo_keywords_exact, qui ne pouvait jamais renvoyer de résultat : la tâche était créée pour un outil Arsenkin nommékeywords_frequency, qui n’existe pas, si bien que chaque appel répondait404 WRONG_TOOL. Le!Wexact passe désormais par l’outilwordstatavectype=1, le tableau des expressions sousqueries, et la région sous forme de liste à un seul élément.ModifiéFonctionneDemander le sélecteur
overalpour!W: les valeurswsd’Arsenkin sont des#720opérateurs distincts…
Demande du sélecteur
#720 (ticket sur GitHub)overalpour!W: les valeurswsd’Arsenkin sont des opérateurs distincts.baseest large (WS),quotedest l’expression entre guillemets (« WS »),overalest la forme exacte d’un mot (!WS), que demande!W, etexactest la forme à ordre strict ([!WS]).ModifiéFonctionneSupprimer avant envoi la ponctuation que le fournisseur refuse : une expression contenant#720
/ou des guillemets…Suppression, avant envoi, de la ponctuation refusée par le fournisseur : une seule expression contenant
#720 (ticket sur GitHub)/ou des guillemets faisait rejeter tout le lot avec422 JSON_VALIDATION_ERROR. Les expressions réécrites sont renvoyées danscleaned, pour que l’appelant voie celles qui ont été mesurées sous une autre forme.ModifiéFonctionneRenvoyer
frequenciessous la forme{phrase: {"base": N, "overal": N}}à côté de la#720charge brute du fournisseur…
Renvoi de
#720 (ticket sur GitHub)frequenciessous la forme{phrase: {"base": N, "overal": N}}à côté de la réponse brute du fournisseur. Une expression dont le résultat ne concerne que d’autres régions est omise et listée danswarnings, au lieu d’emprunter les chiffres d’une autre région.AjoutéFonctionneAccès Topvisor en lecture seule et borné via le CLI partagé et le handler MCP, avec#719
fichiers d’identifiants centralisés…
Accès en lecture seule et borné à Topvisor via le CLI partagé et le handler MCP, avec fichiers d’identifiants centralisés, pagination explicite et aucune opération de contrôle payante.
#719 (ticket sur GitHub)ModifiéFonctionneAlignement du bloc historique
sources.gscdesources-doctorsur#717
#717 (ticket sur GitHub)provider_status.gsc:readyet le nouveau champcomponentsproviennent désormais des mêmes composants d’identifiants (jeton OAuth, autorisation durable ou compte de service), et non plus du seul fichier de jeton. Ainsi, un compte de service configuré n’apparaît plus comme « GSC is not set up ». Le composant compte de service valide désormais le document JSON lui-même (fichier régulier privé, taille bornée, typeservice_account, URI de jeton Google exacte et champs requis). Les deux blocs du doctor renvoient une énumération sûreservice_account_status(missing,unsafe_file,too_large,unreadable,malformed_json,unsupported_shapeouconfigured_unverified). Un fichier trop volumineux, illisible ou mal formé reste ainsi distinct d’un fichier configuré mais non vérifié, sans exposer son contenu.ModifiéFonctionneLes artefacts de scan de
crawl-siteportent désormaisfinished_atet un cycle de vie#712
#712 (ticket sur GitHub)finisheddès que chaque ligne de frontière acceptée atteint un état terminal, y compris pour les exécutions dont les preuves conservées sont partielles.crawl_partialnomme déjà les observations omises : une file vidée ne passe donc plus pour une capture interrompue dansscan-status,scan-listou l’historique (#712). Une exécution arrêtée explicitement (interrompue avant d’avoir accepté du travail, annulée, ou stoppée par le disjoncteur d’erreurs) conserveinterruptedet la raison enregistrée, même après vidage de la file, y compris viaresume_or_finalize: les chemins d’arrêt ne reçoivent donc jamais un horodatage de fin erroné.ModifiéFonctionne
duplicate-check --scanetboilerplate-report --scanlisent désormais un corpus#710conservé en flux au lieu d’échouer sur des sites de taille normale…
#710 (ticket sur GitHub)duplicate-check --scanetboilerplate-report --scanlisent désormais un corpus conservé en flux, au lieu d’échouer sur des sites de taille normale. Le délai d’ouverture et de validation du scan est calculé selon la taille de l’artefact, et chaque lecture de document a son propre délai : un corpus de 1 500 pages ne se termine plus eninterrupted. Le budget d’octets du corpus compte ce que l’analyseur conserve réellement, à savoir le Markdown extrait pour les doublons et une empreinte par page pour le contenu répété, et non la taille du HTML brut. Lorsqu’une borne du corpus est atteinte, la couverture est signalée comme partielle, avec les décomptes exacts préparés et éligibles ainsi que la borne qui a arrêté la lecture (#710).ModifiéFonctionne
report()etby_time()de Metrika rejettent désormaisoffset < 1avec une#707ValueError…
#707 (ticket sur GitHub)report()etby_time()de Metrika rejettent désormais localementoffset < 1avec uneValueError, au lieu de le transmettre à l’API Reporting, qui répondaitoffset=0par HTTP 400. De nouveaux tests hors ligne fixent le contrat à partir de 1 pourmetrika-report,seo_metrika_reportetaggregate_reportdeprovider-collect, et vérifient que les conditions d’arrêt paginées (total_rows,ROW_CAP) comptent les lignes sautées avant unoffsetnon par défaut (#707).
2 octobre 2026
1 ajouté2 modifiéModifiéFonctionne
metrika-traffic-pdfsous macOS : Chrome écrit le PDF et continue de tourner ; le#731navigateur est…
#731 (ticket sur GitHub)metrika-traffic-pdfsous macOS : Chrome écrit le PDF et reste actif ; le navigateur est désormais lancé sans attendre, puis arrêté une fois le PDF vérifié.ModifiéFonctionneYandex Webmaster :
user_idest résolu à partir du jeton ; nouvelles opérations en#713lecture seule…
Yandex Webmaster :
#713 (ticket sur GitHub)user_idest résolu à partir du jeton. Nouvelles opérations en lecture seule :summary,diagnostics,sqi_history,search_history,query_history,in_search_history,events_history,indexing_history,important_urls,broken_links_history,external_links_history. Ajout deparamspour les dates et les indicateurs, et pagination des listes de requêtes et d’échantillons de crawl (#713).AjoutéFonctionneNouveau skill technique
traffic-report: un rapport mensuel ou annuel destiné auclient, à partir de Metrika, Webmaster et Search Console (conversions comptées comme visites, contrôles de cohérence des objectifs, produits, portrait des acheteurs, règles de mise en page des diapositives).
1 octobre 2026
1 ajouté1 modifiéAjoutéFonctionneAjout de
metrika-traffic-pdf(seo_metrika_traffic_pdf) : un rapport de trafic#731statique au format A4 paysage, dans la mise en page d’un tableau de bord analytique, construit à partir de Yandex Metrika. La collecte et le rendu sont deux étapes séparées. La collecte lit l’API Reporting (environ 40 requêtes gratuites, en lecture seule) et produit un seul document
#731 (ticket sur GitHub)seohead.metrika-traffic/1: neuf cartes d’indicateurs (utilisateurs, visites, pages vues, pages par visite, visites par jour, taux de rebond, durée moyenne de visite, part des visiteurs avec deux ou trois visites, part des nouveaux visiteurs), avec leurs variations par rapport à la période précédente de même durée et aux mêmes dates un an plus tôt ; la dynamique quotidienne ; des fenêtres de 3, 6 et 12 mois comparées aux fenêtres qui les précèdent ; les moteurs de recherche, villes, pays, appareils, âge, sexe, principales pages d’atterrissage et expressions de recherche ; et tous les canaux de trafic. Le rendu met ce document en HTML autonome avec des graphiques SVG intégrés, puis l’imprime en PDF via Chrome, Edge ou Chromium installé localement (SEOHEAD_CHROMEremplace la détection). L’identité visuelle (nom, couleurs, pile de polices, texte du logo) et les libellés en anglais ou en russe sont des entrées ; les lignes de requêtes Search Console peuvent être ajoutées comme page facultative. L’attribution par défaut est la dernière source significative (ym:s:lastSignTrafficSourceet ses dimensions de moteur et d’expression), que reflète l’interface de Metrika ; les dimensions au dernier clic s’en écartent de quelques pour cent et restent un choix expliciteattribution=last_click. Chaque requête passeoffset=1, car l’API compte les lignes à partir de 1. Les données manquantes restent visibles : chaque bloc porteok,unavailableouskippedavec une raison ; une variation sans base de comparaison vautnullet non zéro ; l’absence de navigateur laisse le HTML en place et signale le PDF comme ignoré ; les fichiers existants dansout_dirne sont remplacés qu’avecoverwrite=true.ModifiéFonctionneLes rapports Metrika commencent à
offset=1: l’API Reporting est en base 1 et répondait#707à chaque requête…
Les rapports Metrika commencent à
#707 (ticket sur GitHub)offset=1: l’API Reporting compte à partir de 1, et répondait HTTP 400 à chaque requête envoyée avecoffset=0(#707, #728, #729).
26 septembre 2026
1 ajoutéAjoutéFonctionneAjout de la preuve native
HEADING_SKIPpour les sauts de niveau H1–H6 dans la région de#725contenu nommée. Les titres de l’en-tête, du pied de page, de la navigation, de la barre latérale et ceux qui sont explicitement masqués ou situés dans des dialogues fermés sont exclus, afin qu’une interface de gabarit ne crée pas une fausse hiérarchie. Les exécutions en export seul de Screaming Frog signalent ce contrôle comme indisponible.
#725 (ticket sur GitHub)
23 septembre 2026
1 modifiéModifiéFonctionneAccepter les fichiers de secrets privés sous Windows, où les bits de permission POSIX#708
n’existent pas, et ajouter un script de consentement…
Acceptation des fichiers de secrets privés sous Windows, où les bits de permission POSIX n’existent pas, et ajout d’un script de consentement dans le navigateur qui enregistre une autorisation Search Console en lecture seule.
#708 (ticket sur GitHub)
13 septembre 2026
2 ajouté3 modifié2 corrigéAjoutéFonctionnePrise en charge, sous Windows, des modules d’écriture SQLite natifs, de l’historique, des#705
nouvelles tentatives, des instantanés, des nouvelles analyses et de la publication de projets, avec des verrous par plage d’octets fournis par le système et des contrôles d’espace libre portables. Le vidage POSIX des répertoires est conservé ; le vidage des fichiers sous Windows et la publication atomique restent disponibles, sans garantie de fsync de répertoire POSIX. Les workflows portables sont testés dans la CI Windows et dans un environnement Linux isolé.
#705 (ticket sur GitHub)CorrigéFonctionneLe scan HTML en texte brut reste compatible avec les anciennes versions de Python, sans#705
l’option de script récente de HTMLParser…
Le scan HTML en texte brut reste compatible avec les versions de Python qui n’ont pas l’option de script récente de HTMLParser, y compris la distribution Windows de Python 3.12.
#705 (ticket sur GitHub)CorrigéFonctionneCorriger et sourcer les recommandations Google sur les données structurées et Googlebot,#704
y compris la disponibilité FAQ/HowTo…
Correction et sourcing des recommandations Google sur les données structurées et Googlebot, y compris la disponibilité FAQ/HowTo et les conséquences de la politique sur les données structurées.
#704 (ticket sur GitHub)ModifiéFonctionneDécouvrir les sitemaps depuis la racine du site via
robots.txt, avec repli sur#703/sitemap.xml…Découverte des sitemaps à partir de la racine du site via
#703 (ticket sur GitHub)robots.txt, avec repli sur/sitemap.xml. Lorsque l’expansion de tous les sitemaps échoue, le résultat est signaléok: false.ModifiéFonctionneAutoriser la reprise d’un crawl natif après des cookies anonymes posés par le serveur, et#702
enregistrer la nouvelle session…
La reprise d’un crawl natif est désormais possible après des cookies anonymes posés par le serveur, et la nouvelle session est enregistrée sans conserver les valeurs des cookies. Les crawls authentifiés et ceux à profil persistant restent non reprenables.
#702 (ticket sur GitHub)ModifiéFonctionneStandardisation : les chemins de scan enregistrés deviennent
--scan, le JSON inline#701devient
--input; les anciennes…Standardisation : les chemins de scan enregistrés s’écrivent désormais
#701 (ticket sur GitHub)--scan, et le JSON inline--input. Les anciennes formes,--inputavec un chemin de scan et--json-input, continuent de fonctionner pendant une version, avec un avertissement.AjoutéFonctionneAjout d’un consentement explicite, désactivé par défaut, pour conserver des preuves#700
anonymes bornées portant
#700 (ticket sur GitHub)Cache-Control: no-store. Les corps de réponses authentifiées continuent d’être omis.
9 septembre 2026
10 ajouté9 modifié2 corrigéAjoutéFonctionneAjout d’aperçus hors ligne des priorités de projet et d’application explicite et#694
versionnée de la politique, à partir des faits de stack enregistrés. Les choix de l’opérateur sont préservés, l’historique des politiques est atomique, et les explications de priorité correspondantes figurent dans les rapports lisibles.
#694 (ticket sur GitHub)CorrigéFonctionneCorriger les recommandations sur le rendu JavaScript, le canonical, l’hygiène des URL et#692
notranslate, sans modifier les contrôles ni les sévérités…Correction des recommandations sur le rendu JavaScript, le canonical, l’hygiène des URL et
#692 (ticket sur GitHub)notranslate, sans modifier les contrôles à l’exécution ni les niveaux de sévérité.CorrigéFonctionneGarder les URL du sitemap et du robots.txt de la fixture de documentation sur l’origine#690
loopback éphémère.
#690 (ticket sur GitHub)AjoutéFonctionneCouverture portable des checklists de projet, avec un catalogue dérivé des sources et#686
packagé, des modèles personnalisés réutilisables…
Couverture portable des checklists de projet : catalogue dérivé des sources et fourni avec le paquet, modèles personnalisés réutilisables, historique des définitions et des preuves, écritures concurrentes optimistes, revue manuelle explicite et revue des livrables, et vues hors ligne des éléments en attente, obsolètes ou bloqués, partagées entre le CLI, le MCP et les rapports humains liés au projet.
#686 (ticket sur GitHub)AjoutéFonctionneAjout d’un résumé de statut de scan natif en lecture seule pour les populations de la#685
frontière et les résultats HTTP enregistrés.
Ajout d’un résumé de statut de scan natif, en lecture seule, pour les populations de la frontière et les résultats HTTP enregistrés.
#685 (ticket sur GitHub)AjoutéFonctionneAjout d’espaces de travail de projet locaux et portables, avec des états explicites pour#678
les checklists en attente et la préparation.
#678 (ticket sur GitHub)AjoutéFonctionneConsigner dans le dépôt la revue du guide Google Search Central : 175 guides lus, leurs#677
étiquettes, leurs exclusions, la date de l’instantané et les trois correctifs produits…
Consignation dans le dépôt de la revue du guide Google Search Central : 175 guides lus, leurs étiquettes, leurs exclusions, la date de l’instantané et les trois corrections qui en ont découlé.
#677 (ticket sur GitHub)ModifiéFonctionneMode URL de
crawl-site: écriture par défaut d’un scan SQLite natif sans collision sous#676le dossier de l’appelant…
Le
#676 (ticket sur GitHub)crawl-siteen mode URL écrit désormais, par défaut, un scan SQLite natif sans collision dans le dossierscans/de l’appelant.--out-dirreste la voie explicite héritée vers un répertoire ; les entrées en liste ainsi que les configurations de cache en mode live ou replay désignent cette voie, au lieu de changer silencieusement de mode de collecte.ModifiéFonctionneLes builds de distribution propres intègrent désormais une provenance de producteur#675
validée pour les scans SQLite natifs, lorsqu’aucune copie de source n’est disponible.
#675 (ticket sur GitHub)ModifiéFonctionneRemplacer, dans le skill d’audit packagé, les affirmations non étayées sur la perte de#671
signal des 301 et l’absence de transmission des 302…
Dans le skill d’audit packagé, les affirmations non étayées sur la perte de signal fixe des 301 et l’absence totale de transmission de popularité des 302 sont remplacées par une logique d’intention permanente ou temporaire et de cible canonique, avec renvoi à Google Search Central. Les redirections temporaires restent valides pour les changements temporaires ; un code de statut seul ne mesure pas une perte de positionnement.
#671 (ticket sur GitHub)ModifiéFonctionne
render-check --viewport mobileutilise désormais une identité de diagnostic smartphone#670stable pour ses requêtes brutes et ses requêtes navigateur. Un
#670 (ticket sur GitHub)--user-agentexplicite est disponible via la CLI et le MCP, et chaque comparaison terminée ou partielle enregistre l’identité effective et la taille du viewport.AjoutéFonctionneAjout d’entrées
--scanen lecture seule pour l’analyse des doublons et du boilerplate#669sur un corpus, avec couverture du corps conservé et provenance.
#669 (ticket sur GitHub)AjoutéFonctionneCatalogue généré des entrées de commande ajouté, et documentation du stockage du scan, du#668
cache HTTP et du journal d’exécution…
Catalogue généré des entrées de commande ajouté, et documentation de la décision concernant le stockage du scan, du cache HTTP et du journal d’exécution.
#668 (ticket sur GitHub)ModifiéFonctionneLes rapports destinés au client (Markdown, CSV, XLSX, DOCX et tâches) utilisent désormais#660
des intitulés de constats lisibles…
Les rapports destinés au client (Markdown, CSV, XLSX, DOCX et tâches) utilisent désormais des intitulés de constats compréhensibles par le lecteur et les preuves de reproduction enregistrées, au lieu des identifiants du registre ou des noms de collecteurs. Les anciens enregistrements sans preuves suffisantes l’indiquent explicitement, tandis que les artefacts JSON d’audit restent inchangés.
#660 (ticket sur GitHub)AjoutéFonctionneAjout de
project-factspour enregistrer les faits de stack d’un projet après sa#648création, éventuellement à partir d’une passe…
Ajout de
#648 (ticket sur GitHub)project-factspour enregistrer les faits de stack d’un projet après sa création, éventuellement à partir d’une seule passe explicitetech-detectrespectueuse de robots.txt. Les décisions saisies par l’opérateur restent prioritaires sur les indices détectés ; une détection indisponible ou ambiguë reste absente, avec sa raison.ModifiéFonctionneLe README commence par le contrôleur de projet, et Screaming Frog est présenté comme une#648
seconde passe de vérification croisée, et non comme une norme de parité.
#648 (ticket sur GitHub)AjoutéFonctionneLivraison de
examples/project-skeletonavec une checklist initialisée et vierge, pour#648que
project…Livraison de
#648 (ticket sur GitHub)examples/project-skeletonavec une checklist initialisée et vierge :project statusaffiche désormais des compteurs de couverture au lieu denot_initialized, et ce fichier est vérifié par rapport au catalogue en vigueur.AjoutéFonctionneAjout des contrats de préparation de projet, de preuves fournisseur, de corpus#354
sauvegardé, d’extraction bornée, de preuves de scan et de workflows/catalogue, avec des limites explicites sur les preuves conservées et la progression.
#354 (ticket sur GitHub)ModifiéFonctionneLes profils MCP et les wrappers de progression qui préservent les métadonnées exigent le#354
SDK MCP optionnel en version 1.29 ou plus récente.
Les profils MCP et les wrappers de progression qui préservent les métadonnées exigent le SDK MCP optionnel en version 1.29 ou ultérieure.
#354 (ticket sur GitHub)ModifiéFonctionneREADME simplifié autour des workflows tâche → commande → artefact, suppression des
visuels obsolètes et des compteurs marketing…
README simplifié autour des workflows tâche → commande → artefact : suppression des visuels obsolètes et des compteurs marketing, et conservation des liens vers les références générées de l’interface en vigueur.
ModifiéFonctionneREADME enrichi de workflows concrets entrée → résultat, avec les appels CLI/MCP
correspondants et des limites exactes concernant le corpus conservé, le navigateur et les fournisseurs.
8 septembre 2026
8 modifié2 corrigéModifiéFonctionneDocumentation de l’artefact SQLite réalignée sur le corps natif livré, l’historique et#666
les contrats de réanalyse hors ligne…
La documentation de l’artefact SQLite est réalignée sur le corps natif livré, l’historique et les contrats de réanalyse hors ligne (#666). Le tableau des champs collectés tardivement est désormais vérifié contre le mapping de l’importateur : une observation JSONL plus ancienne reste NULL en SQL au lieu de devenir une valeur propre fabriquée.
#666 (ticket sur GitHub)ModifiéFonctionneLes audits natifs signalent désormais meta keywords, canonical secondaire, version HTTP#659
et amphtml comme ignorés lorsque leurs champs source n’ont pas été collectés…
Les audits natifs signalent désormais les contrôles meta keywords, canonical secondaire, version HTTP et amphtml comme ignorés lorsque leurs champs source n’ont pas été collectés.
#659 (ticket sur GitHub)CorrigéFonctionneNe plus lire comme identifiant de l’opérateur un cookie posé par le site dans la voie de#656
rendu JavaScript, et signaler quand un run écarte des DOM rendus (#656)…
Ne plus lire comme identifiant de l’opérateur un cookie posé par le site dans la voie de rendu JavaScript, et signaler explicitement quand un run écarte des DOM rendus (#656). Un navigateur renvoie tout ce que le
#656 (ticket sur GitHub)Set-Cookiedu site lui a donné : dès qu’une page pose un cookie de session puis demande une sous-ressource de même origine (un script, une feuille de style, une image, la forme ordinaire du web), le hook de requête du rendu lisait l’en-têteCookie:sur le réseau et enregistrait le DOM sérialisé de cette page commeomitted/credentialed, sur un run avechttp.credential_headers = []. Chaque rendu crée son propre contexte navigateur, donc la perte est par page et non cumulative : chaque page qui pose un cookie perd son propre DOM, et le run rapporte ses pages, ses liens et ses compteurs d’escalade exactement comme s’il était complet.credentials_usedpour un document rendu ne provient désormais que de ce que le run a été configuré pour envoyer (http.credential_headersou un profil de navigateur persistant), ce quecrawl/sqlite_render.py::_policy_factsdérive déjà et transmet au moteur de rendu ; celui-ci construit son propre client réseau sans identifiant propre, donc le réseau n’avait rien à ajouter. Un crawl avec un identifiant configuré continue d’omettre ces DOM souscredentialed, inchangé, et la valeur du cookie n’est toujours jamais stockée.ModifiéFonctionneLes compteurs de rendu qu’un indicateur
partialne peut pas exprimer sont désormais#656nommés.
#656 (ticket sur GitHub)rendered_bodiesdans un résultat decrawl-siteindique combien de DOM rendus ont été conservés et la raison de chaque DOM écarté ; la CLI l’affiche à côté de la ligne de fin, au lieu de laisser ce renseignement à un indicateur de capacité qui dit « partial » pour un seul DOM manquant comme pour tous. Un run qui n’a rien écarté n’affiche rien de plus. Les compteurs sont relus dans l’artefact de scan : un scan terminé répond donc encore à la même question après coup.ModifiéFonctionneCollecte de
og:urllors d’un crawl natif, au lieu de signaler chaque page comme n’en#654ayant pas (#654).
#654 (ticket sur GitHub)rules.check_oglisteog:urlparmi lesmissing_tagsd’une page dès que le champ est vide, mais le chemin natif ne lisait jamais cette balise : niog_urldansPageRecord, ni colonneog_urldansscan_v1.sql, niOG:URLdans la projection Internal:All quecrawl/evidence.pyconstruit. La colonne n’existait que côté Screaming Frog, oùnormalizerésolvait toujoursOG:URL; un export du site voyait donc la vraie valeur, alors qu’un crawl natif du même site signalait un défaut. C’était un champ non mesuré présenté comme mesuré, sur chaque page de chaque crawl natif, quel que soit le balisage. La balise est désormais lue au même endroit que les trois autres (le parseur la renvoyait déjà sousog:url), stockée danspages.og_url, colonne nullable comme toutes les colonnes ajoutées tardivement, de sorte qu’un enregistrement écrit avant sa collecte reste distinguable d’une page qui n’en déclare aucune, puis projetée enOG:URL. C’est le côté collecte du raccord dont #646 avait corrigé le côté lecture.tests/test_open_graph_seam.pygagne un troisième site de test dont les pages ont Open Graph sansog:title, pour que le constat se déclenche réellement et que sa liste soit lisible : une page déclareog:urlet ne doit pas être citée comme en manquant, l’autre n’en déclare aucun et doit l’être. Le test déterminant audite ces deux pages deux fois, une fois depuis le crawl et une fois depuis un export Internal:All décrivant les mêmes pages, et exige le même verdict. #646 et #654 étaient toutes deux des situations où deux chemins étaient en désaccord, sans test qui les couvre ensemble.CorrigéFonctionneNe plus lire comme identifiant un cookie posé par le site, et signaler quand un run#647
écarte des corps de page (#647).
Ne plus lire comme identifiant un cookie posé par le site, et signaler explicitement quand un run écarte des corps de page (#647). Un crawl partage un seul client HTTP, et ce client conserve un cookie jar : dès qu’une réponse portait
#647 (ticket sur GitHub)Set-Cookie, chaque requête suivante partait avecCookie:, et le writer du corpus la classait comme une récupération authentifiée. Sur un site public avechttp.credential_headers = []etcredentials_acknowledged = false, cela a écarté 33 001 des 40 920 corps de page, et le run rapportait ses pages et ses liens comme s’il était complet. Tout ce qui se calcule à partir du HTML stocké sur ce crawl (détection des quasi-doublons, profondeur et nombre de nœuds du DOM, part du boilerplate, structure des titres dans le corps, réanalyse hors ligne) couvrait alors 7 903 pages, alors que chaque pourcentage au niveau page du même rapport portait sur 40 920 : deux populations présentées comme une seule. Une capture est dite authentifiée lorsque le run a été configuré pour envoyer un en-tête d’identifiant à cet hôte, seul moyen pour un en-tête sensible d’atteindre une requête construite par le crawler, puisquehttp.headersest refusé par la validation de configuration. Un crawl avec un identifiant configuré omet toujours ces corps souscredentialed, inchangé.ModifiéFonctionneLes compteurs qu’un indicateur
partialne peut pas porter sont désormais nommés.#647
#647 (ticket sur GitHub)html_bodiesdans un résultat decrawl-siteindique combien de corps HTML de pages récupérés ont été conservés, et la raison de chaque corps écarté ; la CLI l’affiche à côté de la ligne de fin, au lieu de laisser ce renseignement à un indicateur qui dit « partial » pour un seul corps manquant comme pour quatre cinquièmes d’entre eux. Un run qui n’a rien écarté n’affiche rien de plus. Les listes d’en-têtes de requête et de réponse expurgées conservent désormais les noms des en-têtes retirés par l’expurgation, sousx-seohead-redacted-headers, jamais leurs valeurs. Auparavant, deux réponses dont les corps avaient été traités différemment enregistraient des listes d’en-têtes identiques octet pour octet, si bien qu’un opérateur lisant l’artefact ne pouvait pas savoir pourquoi un corps manquait.ModifiéFonctionneLes entrées du changelog sortent de
CHANGELOG.md: un fichier par modification sous#638changelog.d/…
Les entrées du changelog sortent de
#638 (ticket sur GitHub)CHANGELOG.mdpour devenir un fichier par modification souschangelog.d/, afin qu’une pull request n’entre plus en conflit avec toutes celles fusionnées avant elle (#638). Chaque branche ajoutait son entrée en tête de## Unreleased, si bien que cette seule ligne était le point de contention du fichier : neuf résolutions manuelles réparties sur huit pull requests en deux sessions, chacune suivie d’un nouveau push et d’une nouvelle CI, avec toujours la même résolution, « garder notre bloc, puis le leur ». Un conflit dont la réponse ne varie jamais ne porte aucune information ; c’est seulement une occasion de perdre un paragraphe en pensant à autre chose. La correction de.gitattributesproposée dans l’issue,CHANGELOG.md merge=union, a été mesurée puis rejetée : union fusionne ligne à ligne, donc quand deux entrées contiennent une même ligne (une ligne vide dans une entrée suffit), elle en garde une copie et annonce un succès, en supprimant la ligne de l’autre entrée sans conflit et sans que personne soit invité à vérifier. Un premier test avec des lignes volontairement distinctes avait fusionné proprement et masqué le problème. Remplacer un conflit bruyant par une perte silencieuse de données est pire que le conflit ; la correction structurelle est donc celle qui a été livrée.ModifiéFonctionneUn fragment est nommé d’après l’issue qu’il ferme (
changelog.d/638.md), la clé propre à#638chaque branche…
Un fragment est nommé d’après l’issue qu’il ferme (
#638 (ticket sur GitHub)changelog.d/638.md), clé unique à chaque branche par construction : une pull request qui ferme deux issues écrit deux fragments, un par issue, et une modification qui n’en ferme aucune écritno-issue-<slug>.md. Un fragment contient l’entrée exactement telle qu’elle doit figurer dans le changelog (une ou plusieurs puces de premier niveau avec leurs lignes de suite, sans front matter ni enveloppe), pour que les entrées en prose multi-paragraphes de ce fichier soient restituées octet pour octet, et non réduites à des lignes uniques.python scripts/build_changelog.pyles intègre dans## Unreleasedau moment de la release, ordonnées par numéro d’issue et non selon l’ordre que rend le système de fichiers, et--prunesupprime les fragments intégrés. L’assemblage remplace toute la zone comprise entre deux commentaires de marquage au lieu d’y insérer, donc l’exécuter deux fois produit le même fichier, que les fragments aient été supprimés entre-temps ou non.CHANGELOG.mdlui-même n’est pas régénéré à chaque pull request, délibérément : une garde qui le maintiendrait constamment synchronisé remettrait les deux branches dans le même fichier et recréerait le conflit que cela supprime.ModifiéFonctionneLes gates de documentation qui lisaient
CHANGELOG.mdétaient le risque : une garde qui#638cesse discrètement de contrôler…
Les gates de documentation qui lisaient
#638 (ticket sur GitHub)CHANGELOG.mdétaient le risque, car une garde qui cesse discrètement de contrôler est pire que les conflits. Les deux ont été repérées et couvrent désormais les fragments.tests/test_docs_drift.pycontenaitCHANGELOG.mddansPUBLIC_MARKDOWN: la garde de langue anglaise seule, celle des « références aux seules commandes existantes » et celle de dérive des compteurs lisent donc aussichangelog.d/*.md, et les deux gates de dérive des compteurs ignorent un fragment pour la même raison qu’elles ignorent le changelog : une entrée enregistre ce qui était vrai au moment de son écriture.tests/test_doc_counts.pyexclut délibérément le changelog de son analyse des compteurs en direct, et sa liste de fichiers n’atteint jamaischangelog.d/, ce qui répond de la même façon pour la même raison ;tests/test_doc_commands.py,tests/test_docs_commands_execute.pyettests/test_examples_gate.pyne lisent jamais le changelog. Un nouveautests/test_changelog_fragments.pyfixe chacune de ces réponses, ainsi que la nomenclature des fragments, l’ordre déterministe, l’assemblage idempotent, et le fait que deux branches concurrentes simulées, ajoutant chacune un fragment, fusionnent sans conflit.
Antérieur (date non enregistrée)
23 ajouté30 modifié17 corrigéCorrigéFonctionneContinuer d’identifier une page dont le site envoie un en-tête
Set-Cookiesans valeur(#651).
Un en-tête sans paire
name=value, commeSet-Cookie: Secure; HttpOnlyqueemall.byenvoie à côté de deux cookies bien formés, devientCookie(name='Secure', value=None)dans le jar, ethttpx.Cookies.__getitem__lèveKeyErrorpour lui.dict(resp.cookies)échouait donc sur un nom que l’itérateur même du mapping avait produit :tech-detectsortait avec le code 1 et un simpleerror: 'Secure', sans JSON sur stdout, pour une page pourtant récupérée avec succès ; le MCPseo_tech_detectrelançait cetteKeyErrorà son appelant, etsite-auditrenvoyait silencieusement une section technologies absente.detect_techparcourt désormais le jar de cookies. Un nom sans valeur est conservé avec une valeur vide plutôt qu’abandonné, car plusieurs empreintes (BITRIX_SM_GUEST_ID,craft_session,wfvt_) ne reconnaissent un cookie que par son nom, et l’abandonner ferait perdre ce signal. Mais il n’est jamais normalisé silencieusement en cookie ordinaire : les noms sont signalés dansmalformed_cookieset cités dans un constat, de sorte qu’un en-tête mal formé envoyé par le site reste un fait sur le site. Une réponse ne contenant que des cookies bien formés n’est pas affectée et renvoie une liste vide.ModifiéFonctionne
render-checkmesure de nouveau les sites compressés au lieu de les déclarer cassés(#650).
La route de rendu épinglée lisait l’origine via le flux brut non décodé de httpx et transmettait ces octets à Playwright avec le
content-encodingde l’origine.route.fulfilln’applique jamais un codage de contenu au corps qu’on lui donne : Chromium interprétait le gzip comme dutext/htmlet construisait un DOM à partir du flux compressé, sans title, sans h1, sans canonical, sans liens, avec une taille rendue égale à celle du transfert. Tout site qui compresse son HTML, c’est-à-dire presque tous, sortait avecok: false, reason: incomplete_render, et le message accusait le rendu en suggérant--wait domcontentloadedou un--timeoutplus long, alors que le rendu était parfaitement terminé.seohead.techannonçait 22 435 octets rendus contre 121 793 bruts ; il en annonce désormais 123 048, avec le vrai title et 31 liens. La route lit désormais l’entité décodée, retirecontent-encodingde ce qu’elle transmet (content-lengthétait déjà retiré comme hop-by-hop, donc Playwright annonce la longueur du corps réellement reçu), et ne demande à l’origine que les codages que ce client sait décoder, au lieu de relayer l’invitation de Chromium àbretzstd. Un codage qui arrive quand même non décodé interrompt la requête en le nommant, car le laisser passer reproduirait le bogue en silence. Le plafond d’octets compte désormais les octets décodés, c’est-à-dire le corps que voit le moteur de rendu : une bombe de compression passait auparavant le plafond sous sa forme compressée.ModifiéFonctionneLecture d’Open Graph sur un crawl natif, avec la clé que le parseur écrit réellement
(#646).
parse_htmlenregistre chaque balise sous son nom de propriété complet,{"og:title": ...}, comme l’indique sa docstring, etcrawl/collect.pyinterrogeait le même dict avec"title","description"et"image".dict.getrépondaitNoneà chaque fois :og_title,og_descriptionetog_imageétaient vides sur chaque page de chaque crawl natif jamais lancé. Lors du run qui a révélé le problème, 0 page sur 40 920 en portait une, sur un site dont les pages publient pourtant Open Graph. C’est la règle centrale du dépôt inversée : un champ non mesuré lu comme mesuré. Le défaut s’est propagé, carcrawl/evidence.pyprojette ces colonnes dansOG:Title,OG:DescriptionetOG:Imagede la trame Internal:All : un contrôle voyait du vide sur un crawl natif et la vraie valeur sur un export Screaming Frog du même site. Le contrat d’honnêteté decheck_ogmasquait le dégât au lieu de le montrer : sans colonne OG renseignée nulle part, il passait en « ignoré », si bien qu’un site avec Open Graph s’auditait exactement comme un site sans. Le parseur est inchangé ; seul le côté lecture a bougé. Un nouveautests/test_open_graph_seam.pycrawle deux sites de test en loopback et suit la valeur du parseur jusqu’àpages.jsonl, puis à la tablepagesde l’artefact de scan, puis aux colonnesOG:*de la trame. Il crawle un second site sans Open Graph pour distinguer « vide parce qu’absent » de « vide parce que non lu », et fixe la forme des clés du parseur dans le même fichier. La panne tenait à deux modules corrects en désaccord sur un nom de clé, sans test qui couvre les deux.ModifiéFonctionneRépondre à « ce site est-il bien maillé, et où le maillage est-il défaillant » : le
registre passe de 157 à 161 contrôles, avec une skill internal-linking…
Répondre à « ce site est-il bien maillé, et où le maillage est-il défaillant » : le registre passe de 157 à 161 contrôles, avec une skill
internal-linking(#634). Les éléments existaient (chaque arête avec sa position, son ancre et son drapeau nofollow dans la tablelinks,ORPHAN_PAGE,INLINK_BOILERPLATE_ONLY,LOW_LINK_SCORE), mais rien ne les transformait en réponse sur le graphe. Un audit de 40 920 pages a affiché pendant des heures le mauvais titre à cause de cela : 66 % du sitemap était signalé inatteignable par les liens internes, chiffre produit en soustrayant le nombre d’URL du crawl à celui du sitemap, ce qui mesure « le crawler n’y est pas arrivé ». En parcourant les 5 252 235 arêtes de l’artefact depuis la page d’accueil, l’atteignabilité ressort à 91,9 %. Le vrai défaut était la profondeur : 23 742 pages, soit 58 % du site, se trouvaient à plus de dix clics de l’accueil, et la plus profonde à 3 005, parce que l’archive est une liste chaînée de liens « article suivant » plutôt qu’un arbre. La profondeur de clic est désormais le titre affiché par l’audit.summary.internal_linkingporte l’histogramme, le maximum, la répartition atteignable/inatteignable, les arêtes internes par position de page et le nombre d’arêtes qui en répètent une autre, etaudit.mdl’affiche à côté du résumé de santé. Le parcours part de l’URL à partir de laquelle le crawl a réellement commencé, jamais depages.crawl_depth: cette colonne enregistre où le crawler est tombé sur une URL, et un crawl amorcé par sitemap y inscrit 0 pour la plupart des pages, donc un parcours amorcé depuis elle décrirait un autre endroit. Si aucune URL de départ n’a été enregistrée et que plus d’une page revendique la profondeur 0, la mesure refuse de tourner et dit laquelle.ModifiéFonctionneQuatre contrôles lisent ces chiffres.
DEEP_CLICK_DEPTHse déclenche sur une page indexable dont le plus court chemin depuis l’URL de départ dépassethresholds.click_depth_max(10 par défaut ; chaque constat cite le nombre utilisé).DUPLICATE_INTERNAL_LINKse déclenche sur une page qui écrit plusieurs fois la même destination et la même ancre : sur le site ci-dessus, 509 744 arêtes sur 1 134 307, soit 44,9 %, étaient des répétitions. Il s’agissait d’un même bandeau émis deux fois, la seconde copie étant supprimée par JavaScript, un défaut de gabarit qu’il a fallu un script écrit à la main pour trouver.LINK_INSIDE_HEADINGetIMAGE_LINK_WITHOUT_TEXTont besoin de ce que la zone de page d’un lien ne peut pas porter : la chaîne d’ancêtres de l’ancre et son contenu. Un crawl natif enregistre donc désormais unlink_placement_jsonpar page, petit et plafonné, à côté du plan des titres, et ces deux contrôles passent par leur nom sur un export Screaming Frog au lieu de tourner à vide.ModifiéFonctionneLes règles d’honnêteté du reste de l’analyseur s’appliquent partout.
Une arête sans position enregistrée est comptée en
unclassifiedet n’est jamais rattachée àcontent:link_position.classifyest désactivé par défaut, ce qui laisse chaque arête dans cet état, et une exécution dans cet état doit se lire comme « personne n’a regardé », non comme un site dont tous les liens sont du texte de corps. Une position absente de la distribution est rapportée comme « aucune arête classée ici », et non comme une zone qui manque au site, car les règles sont essayées dans l’ordre et un menu dans<header>correspond d’abord ànav. Un crawl partiel retire le verdict de profondeur de clic, qu’il ait déclenché ou non, parce qu’une frontière jamais récupérée peut contenir le chemin plus court ; un contrôle ne trouvant rien en silence se lirait comme « toutes les pages sont dans le seuil ».CorrigéFonctionneNe plus déclarer propre un contrôle conditionné par une exigence, lorsqu’il n’a jamais
été évalué (#635).
H2_MISSINGne se déclenche que si une configuration définitrequirements.require_h2, et la valeur par défaut est false : sur un run ordinaire, sa branche est inatteignable. Cela ne laissait aucune trace. La couverture le comptait parmi les contrôles silencieux, la catégorie qui signifie « passé sur chaque page sans rien trouver », et le lecteur apprenait que le site avait réussi un contrôle que personne n’avait exécuté. Sur l’audit qui a révélé le problème, 61 % des pages du site n’avaient aucun H2, et 5 066 d’entre elles dépassaient 800 mots : le contrôle se serait déclenché des milliers de fois. Il est désormais déclaré ignoré par son nom, avec une raison qui cite le réglage qui l’a désactivé. La réponse est donc « non évalué, et voici comment l’évaluer », au lieu d’un silence.require_canonicala la même forme et reçoit le même traitement ; sa valeur par défaut étant true, il ne produisait pas encore une lecture fausse, mais il ne pouvait plus commencer à le faire.CorrigéFonctionneCorrection du titre du backlog de tâches affichant « Audit Tasks – None » pour tout audit
dont le run…
Correction du titre du backlog de tâches qui affichait « Audit Tasks – None » pour tout audit dont le run n’avait enregistré aucun projet (#640).
render_tasks_mdconstruisait le titre avecsrc.get("project", "site"), mais"project"est toujours une clé présente dans le blocsourcedu backlog : elle est définie à la construction à partir derun.get("project"), donc un run sans projet stocke la valeurNoneau lieu de laisser la clé absente, et la valeur par défaut dedict.getne s’applique qu’à une clé manquante, jamais à une clé contenantNone. Un crawl natif comme une réanalyse laissantprojectvide, chaque backlog produit à partir de l’un ou de l’autre recevait pour titre le mot « None » littéral. Le titre retombe désormais suror, qui attrape bien unNonestocké, puis cherche l’URL de départ (start_url) ou la source (source) avant d’abandonner. C’est le même ordre queseohead.reports.facts.crawl_domainutilisait déjà pour nommer un site à partir de son blocrun; cette fonction est désormais exportée et réutilisée ici au lieu d’être réinventée, si bien qu’un backlog issu d’un audit sans projet nomme le site réel au lieu du mot « site ».seohead.reports._normalize_sf_auditavait la même forme latente une ligne plus loin (run.get("project") or "", dansreports/__init__.py) : déjà protégée contre unNonestocké, mais son repli était un domaine vide au lieu de l’URL que le run connaît aussi. Le titre « SEO Audit: » des documents docx et md devenait donc vide dans les mêmes situations. Il passe désormais par la même résolutioncrawl_domain.ModifiéFonctionneLecture du plan des titres comme séquence et comme carte de la page : le registre passe
de 155 à 157 contrôles (#632). Les huit contrôles existants sur les titres les jugent comme un ensemble : ils peuvent dire qu’une page a un H1 et quatre H2, et rien de plus. Une page dont l’ordre DOM est
H2, H2, ..., H1, H2satisfaisait donc tous ces contrôles, tout comme une page dont les H2 étaient tous des libellés de menu dans son bandeau. Ce cas a été constaté sur un site réel : un gabarit d’article avec dix-huit titres placés avant son propre H1, signalé propre pour les titres. Il manquait la preuve, non la règle :pagesstockaith1,h1_2eth2, soit trois chaînes sans ordre ni place. Un crawl natif enregistre désormais tout le planh1àh6dans l’ordre du DOM, chaque titre avec son niveau, son texte et sa zone de page, dans une nouvelle colonne nullableheading_outline_json, etHEADING_BEFORE_H1etHEADING_IN_PAGE_CHROMEle lisent. La zone réutilise la taxonomie déjà utilisée pour classer les liens (nav,header,sidebar,footer,content,other), de sorte que « dans l’en-tête » signifie la même chose pour un titre que pour un lien. Un titre qu’aucune règle de position ne reconnaît n’est placé dans la zone de contenu que si le document en nomme une : un<main>, un[role=main], un<article>ou un sélecteur configuré. Quand la racine de contenu retombe sur le<body>entier, qualifier un titre de « contenu » ne voudrait pas dire plus que « quelque part sur la page ». La zone est donc enregistrée comme non mesurée, et la page est citée parmi les contrôles ignorés du run avec cette raison, plutôt que déclarée propre ou signalée comme défaut. L’ordre ne demande pas de zone, doncHEADING_BEFORE_H1répond toujours pour ces pages. Un export Screaming Frog ne contient aucun plan, et les deux contrôles y sont ignorés par leur nom.CorrigéFonctionneConserver une collecte terminée lorsque l’audit qui suit plante (#627).
Un crawl natif d’un site en production a passé vingt minutes à rassembler 600 pages dans un artefact
--scan-out, puis a affichéerror: 'rel_next_2': lastr()d’uneKeyErrorest la clé et rien d’autre. Ni phase, ni traceback, ni signe que chaque page collectée se trouvait complète et réanalysable dans le fichier. La collecte enregistre ses lignes et se referme avant le début de l’audit, donc toute exception levée après ce point relève de l’analyseur et jamais du crawl. Une exception inattendue à cet endroit est désormais nommée par phase, porte le type de l’exception en plus de son message, et renvoie vers l’artefact conservé ainsi que la commande qui le réanalyse, exactement comme le chemin des audits trop volumineux le faisait déjà. Le run signaleaudit_available: falseavec cette raison au lieu de quitter sur l’exception, etcrawl-sitesort avec le code 2. C’est la troisième réponse déjà utilisée pour la contradiction du log-scan : 0 se lirait comme « l’audit a tourné et a trouvé ceci », et 1 comme « la commande n’a rien produit », et aucune de ces deux lectures n’est vraie pour un crawl dont les preuves ont survécu. LaKeyErrorelle-même ne s’est pas reproduite : la même commande sur le même site, sur la branche principale actuelle, s’est terminée. Cela modifie donc ce qui se passe quand un audit échoue, et non ce qui a provoqué cet échec.CorrigéFonctionneNe plus perdre l’audit d’un crawl terminé à cause d’un budget d’inspection fixe de 30
secondes à la réouverture (#631).
NativeScan.open()lit l’en-tête d’un scan natif viainspect(), qui exécute aussiPRAGMA quick_checketPRAGMA foreign_key_checksur toute la base. C’est un vrai contrôle de sûreté, mais dont le coût croît avec l’artefact alors que le budget, lui, ne croissait pas. Un crawl de 3 h 30 et 40 920 pages, qui avait fini de collecter tout ce qu’il devait collecter, n’a produit aucun audit : rouvrir l’artefact résultant de 1 Go demandait environ 48 secondes de validation, contre un budget fixé à 30. Pire, l’échec ne laissait aucune raison de soupçonner un budget. L’abandon à l’échéance remonte de SQLite sous la forme nuesqlite3.OperationalError("interrupted"), enveloppée telle quelle encannot inspect native scan: interrupted, indiscernable d’un fichier réellement corrompu. Les deux points sont corrigés. Le budget par défaut dérive désormais de la taille de l’artefact : un plancher de 30 secondes pour les petits fichiers, plus une marge généreuse par octet, bien en deçà du débit de validation mesuré d’environ 22 Mo/s, puisqu’il s’agit d’un plafond contre une lecture bloquée et non d’un objectif de performance. Untimeout_secondsexplicite fourni par l’appelant l’emporte toujours. Un abandon à l’échéance lève désormais uneScanErrorqui nomme le budget, le temps écoulé et la taille de l’artefact, afin qu’un opérateur distingue un gros fichier lent d’un fichier corrompu, au lieu de lire « interrupted » comme une erreur de sa part.AjoutéFonctionneAjout de
crawl-site --resume <scan.sqlite>(#619).NativeScan.resume_snapshot(), la tableresume_stateet le writer qui la remplit existaient depuis la conception de l’artefact de scan, etcrawl_to_scanpoursuivait déjà un fichier existant. Mais cela ne valait que pour un appelant qui reproduisait octet pour octet les réglages effectifs d’origine, et aucune interface ne le permettait. Pointer--scan-outvers un artefact interrompu avec un seul drapeau modifié était refusé comme incohérence de configuration ; en pratique, un crawl interrompu (6 516 pages sur 34 635, sur le site de quelqu’un d’autre) devait repartir de zéro.--resumeprend l’artefact et rien d’autre : l’URL de départ et la configuration effective complète sont relues depuis le fichier, car c’est sur elles que la frontière stockée a été construite. Tout autre drapeau modifiant le crawl passé avec lui est refusé plutôt qu’appliqué. Un scan produit par une autre version, enregistré pour une autre URL de départ, déjà terminé, dérivé d’une réanalyse, ou crawlé avec des en-têtes d’identifiants qu’il ne peut stocker que masqués, est refusé par son nom à partir du seul fichier, avant qu’une requête ne soit envoyée.crawl-siteaffiche désormais aussi une ligne sur stderr à la sortie, indiquant si le crawl s’est terminé ou arrêté prématurément, pourquoi, et comment le reprendre. Font exception les arrêts après un budget d’URL ou de durée, que la reprise relit aussi et ne peut donc pas passer : la ligne le dit alors au lieu de proposer une commande sans effet.run.crawl_resumedreste l’enregistrement durable : un crawl repris du site de test hors ligne récupère chaque URL exactement une fois à travers les deux processus, et produit un audit identique à celui d’un crawl sans interruption, dans chaque champ sauf celui-là.CorrigéFonctionneNe plus déclarer propre une page non rendue avec
render-check, ni abandonner lespreuves HTML brut lors d’un rendu raté (#642). Deux conséquences du garde de #623, toutes deux dans
render.py. D’abord, un rendu qui a expiré à son jalon de chargement retombe sur la lecture du DOM àdomcontentloaded, ce qui est sain en soi. Mais quand aucun script n’avait tourné à ce moment-là, le DOM rendu était égal au HTML brut : le comparateur ne trouvait rien, et le run renvoyaitok: True,js_dependent: Falseet l’unique constat « Le HTML brut et le DOM rendu sont matériellement équivalents ; le rendu JavaScript ne détermine pas le contenu visible pour le SEO », classé en simple notice, à propos d’une page que personne n’avait rendue.wait_reachedenregistrait la vérité, mais le rapport de l’audit de site ne reprend que le texte des constats, donc la vérité n’arrivait jamais. Un run revenu à un jalon antérieur le dit désormais dans la liste des constats elle-même, retient le verdict de conformité, et renvoiejs_dependent: None(ce run ne sait pas) au lieu deFalse. Cela se déclenchait avec le--wait loadpar défaut, et pas seulement avec unnetworkidledemandé explicitement. Ensuite, le garde renvoyait son unique énoncé « la comparaison est indisponible » avant d’atteindre la branche de coquille vide, et le retour anticipé derender_checkomettait complètement la cléempty_shell. Ainsi, un point de montage vide d’application monopage, quedetect_empty_shell()lit dans la réponse serveur brute et qui n’a besoin d’aucun navigateur, était jeté avec le rendu raté. Comme une coquille SPA est précisément la page dont le rendu expire, le constat se perdait justement là où il comptait. Un constat tiré de la moitié brute survit désormais à un rendu inachevé ; seuls les constats tirés de la moitié rendue sont supprimés. Dans un crawl, une sonde de rendu qui n’a abouti à aucun verdict atterrit désormais danspatterns_unprobedavec sa raison (canal de #626), au lieu de compter comme un motif mesuré et jugé sans besoin de rendu.ModifiéFonctionneSignaler un robots.txt illisible comme un fait sur le site, et non comme une erreur
interne (#629).
EMPTY_ROBOTS, le substitut que_fetch_robotsrenvoie chaque fois que robots.txt n’a pas pu être lu, gardait la forme de robots analysé d’avant l’ajout de groupes et de sitemaps au parseur. Le chemin de succès renvoie la forme deparse_robots, seule forme que le contexterobots_summaryde l’artefact de scan accepte. Donc en mode--scan-out, un crawl s’interrompait avecnative parsed robots summary is invaliddès que robots.txt était injoignable, répondait 429 ou 5xx, redirigeait hors de l’hôte, bouclait, dépassait le budget de sauts, ou renvoyait un corps nontext/plain. Il en allait de même pour un simple 404, qui n’est pas une erreur mais le cas RFC 9309 « aucune restriction », le plus fréquent des sept. Les deux issues documentées étaient donc perdues derrière cette seule ligne. Un robots.txt absent autorise de nouveau un crawl sans restriction, et un robots.txt indisponible arrête le run en indiquant laquelle des six façons d’échouer s’est produite. L’ancien mode de crawl par répertoire ne s’en apercevait jamais, car rien ne lisait les anciennes clés. Le défaut a été trouvé sur un site réel dont le robots.txt redirige en 302, pour tout user-agent non reconnu, vers un point de synchronisation de cookies.CorrigéFonctionneCorrection de
escalate()qui comptait une sonde de rendu échouée comme « ce motif n’apas besoin de rendu » (#626).
escalate()échantillonne une URL par motif de gabarit pour décider si ce motif a besoin d’une récupération plus complète, avec rendu JavaScript. Mais quand tous les échantillons d’un motif échouaient à la sonde (échec de lancement du navigateur, délai dépassé, ou rendu incomplet, soit les modes d’échec ordinaires d’un crawl réel), lefor/elseenregistrait tout de même le motif comme sondé. Il n’était ni escaladé ni listé parmi les motifs que le run n’avait pas pu évaluer. Cela inversait la règle centrale du projet, selon laquelle une preuve manquante ou non mesurée ne doit jamais se lire comme propre : un motif sur lequel on n’avait rien appris était récupéré en statique en silence, et présenté comme un motif réellement mesuré et jugé sans besoin de rendu.EscalationResultsignale désormais un tel motif danspatterns_unprobed, aux côtés des motifs coupés par l’échéance déjà suivis là, et enregistre la raison d’échec de la sonde dans le nouveau champpatterns_unprobed_reasons. Un rapport peut ainsi dire « ce motif n’a pas été évalué » au lieu de le laisser entendre. Un motif ayant au moins une sonde réussie n’est pas affecté : cela ne change que ce qui se passe quand toutes les sondes d’un motif renvoientok: False.CorrigéFonctionneArrêt de la déclaration d’un rendu inachevé comme défaut du site par
render-check(#623).
Lorsque le rendu n’a pas abouti, l’instantané rendu revenait sans title, sans
h1, sans canonical, avec zéro lien et un cinquième des octets de la réponse brute. Le comparateur lisait cette vacuité comme une preuve sur le site : il émettait « le title change après JavaScript » à propos d’un title que le rendu n’avait jamais lu, et « le canonical est injecté par JavaScript » à propos d’un canonical qu’il n’avait jamais vu. Sur un audit réel, cela faisait quatre faux constats d’affilée, chacun du type qui envoie un développeur chasser un bogue JavaScript inexistant. La paire est désormais jugée avant d’être comparée. Quand la réponse brute contient des repères et que le document rendu n’en contient aucun, pour une fraction seulement de sa taille, la vérification renvoieok: falseavecreason: "incomplete_render", une raison nommée portant les deux tailles en octets, et les deux instantanés à inspecter. Aucun constat n’est émis, etjs_dependent: nullremplacefalse, parce que le run ne sait pas.site-auditrange cela danssummary.tools_failed, là où appartient un contrôle non mesuré. La détection est volontairement étroite : un seul repère survivant, ou un document rendu d’au moins la moitié de la taille de la réponse brute, est comparé comme avant. Une page sans title ni lien des deux côtés est mesurée et simplement vide, non non mesurée. Un title réellement réécrit par un script déclenche toujours son constat. La stratégie d’attente gère aussi la cause ordinaire : un site dont les scripts d’analytics, de chat ou de publicité en long polling ne laissent jamais le réseau au repos expirait en entier sous--wait networkidle. Il retombe désormais sur la lecture du DOM àdomcontentloaded, sans seconde navigation, avecwait_reachedindiquant le jalon exact d’où provient l’instantané, plus une courte pause pour les scripts différés. Une page dont DOMContentLoaded ne se déclenche jamais reste un échec de rendu, et non un rendu incomplet.ModifiéFonctionneAjout d’une ligne de progression en direct à
crawl-site(#619, volet progression).Un crawl natif affichait son taux de requêtes effectif, puis rien du tout jusqu’à la fin. Sur un site de 34 000 pages, la seule façon de savoir qu’il tournait encore était de regarder grossir le fichier de scan avec
ls -la. La ligne se rafraîchit désormais sur place dans un TTY : pages récupérées, URL connues jusqu’ici, leur pourcentage, le débit courant sur une fenêtre glissante, le temps écoulé, et la taille de l’artefact de scan quand--scan-outen écrit un. Elle évite délibérément de suggérer un total qu’elle ne peut pas connaître. Un crawler découvre sa propre charge de travail, donc le dénominateur estfetched + queuedplafonné au budget d’URL. Il est étiquetéknownet nontotal, et une ligne d’en-tête affichée une seule fois précise que la frontière grossit encore et que le pourcentage n’est pas une estimation de la fin du run. Un débit non mesurable s’afficherate n/a, jamais0.0 req/s. Les deux chiffres viennent des structures du crawl lui-même : pour un scan SQLite, de la même requêteresume_snapshotque la boucle de collecte utilise pour se diriger. L’écran affiche donc ce que l’artefact contient à cet instant, et non un décompte parallèle susceptible de diverger. Hors TTY (un tube, un fichier journal, la CI), une ligne simple toutes les 30 secondes remplace les redessins avec retour chariot. Une nouvelle option-q/--quietsupprime à la fois la ligne de progression et la ligne de débit au démarrage, en laissant intact le résultat JSON sur stdout.ModifiéFonctionneClôture des trois lignes de pagination laissées ouvertes par #385 : le registre passe de
152 à 155 contrôles.
PAGINATION_MULTIPLEetPAGINATION_URL_NOT_IN_ANCHORsignalent une page qui déclare deux successeurs différents, ou dont l’URL déclarée n’est pas aussi une ancre de la même page. Les successeurs sont comparés après normalisation d’URL, la même identité que la moitié « ancre » utilise. Ainsi/blog/page/2et/blog/page/2/venant de deux extensions forment un seul successeur écrit deux fois, et non une série ambiguë.PAGINATION_URL_NOT_IN_ANCHORa besoin de tout l’inventaire de liens d’une page et lit pour cela l’export All Inlinks.PAGINATION_MULTIPLEn’en a pas besoin : il lit désormais d’abord les colonnes plus légères d’occurrencesrel="next" 2/rel="prev" 2deInternal:All, commeCANONICAL_MULTIPLErépond déjà à partir deCanonical Link Element 2. All Inlinks n’est qu’un repli pour un profil dontInternal:Alla été écrit sans ces colonnes.PAGINATION_SEQUENCE_ERRORsignale une rupture dans une suite de numéros de page que la série suit par ailleurs, selon la mise en garde de l’issue elle-même : une série peut commencer à un numéro autre que un, un pas n’est pas une rupture, et une série dont les URL n’indiquent pas de numéro de page est déclarée non évaluée, plutôt que jugée au regard d’une numérotation qu’il aurait fallu inventer. Chaque série laissée sans jugement est citée parmi les contrôles ignorés du run, avec la raison valable pour *cette* série. Un pas, un cycle, une série trop courte pour contenir une suite et un numéro de page illisible sont quatre affirmations différentes, et trois d’entre elles décrivent des séries dont toutes les URL affichent leur numéro. Le décompte se fait par série et non par run, si bien que la forme ordinaire de WordPress (page un à/blog/sans numéro, les suivantes à/blog/page/N/) ne peut pas disparaître derrière une série jugeable ailleurs sur le même crawl.AjoutéFonctionneAjout d’une garde de couverture des verdicts de contrôle (#98) :
test_check_producer_gate.pyprouvait que chaque contrôle…Ajout d’une garde de couverture des verdicts de contrôle (#98).
test_check_producer_gate.pyprouvait que chaque identifiant de contrôle est enregistré et que chaque fonctioncheck_*est appelée, mais ne disait rien de la question de savoir si la conclusion d’un contrôle avait jamais été démontrée vraie face à un balisage portant réellement le défaut, ni de son silence face à un balisage qui ne le porte pas. C’est exactement la lacune qui a laissé passer #94, #95 et #96 malgré chaque test existant, tout en continuant de se déclencher à tort sur des sites réels.test_check_verdict_coverage.pyparcourttests/structurellement (AST, et non une liste fixe d’appels) pour les deux moitiés de chaque contrôle. Il fait échouer le build pour tout contrôle nouvellement ajouté au registre sans test bilatéral ni exemption nommée et motivée. Sur les 152 contrôles du registre, 88 avaient déjà une preuve bilatérale. Un nouveautest_check_verdict_gaps.pycomble 22 autres lacunes peu coûteuses à tester, révélées par le scan : longueur et duplication des titres et descriptions, duplication de H1, présence de H2, hygiène des URL, contrôles de directives et de balisage, codes de réponse, et les deux contrôles d’exports filtrés natifs. Les 64 restants sont nommés, non cachés, dans le cliquetKNOWN_UNCOVEREDde la garde, à réduire au fur et à mesure que la couverture arrive. Cette liste ne doit jamais grandir pour admettre un contrôle ajouté après l’existence de cette garde.CorrigéFonctionneCorrection d’un défaut de sélection de groupe dans robots.txt : une valeur
User-agentvide annulait la politique par défaut d’un site (#566).
Une ligne
User-agent:nue, ou dont le nom de bot était avalé par un commentaire en ligne (User-agent: # old bot rule), était analysée en jeton vide._rules_fortraitait ce jeton comme n’importe quel nom littéral. C’est un préfixe de longueur zéro de tous les agents, donc il correspondait à tous, et comme il comptait comme une correspondance *nommée*, il l’emportait sur le vrai groupeUser-agent: *du fichier pour chaque crawler que le fichier ne mentionnait pas. Un robots.txt dont la seule règle substantielle étaitUser-agent: * / Disallow: /apparaissait donc entièrement explorable, tant dans le rapport d’accès des bots d’IA que dans le filtrage de récupération du crawler lui-même, qui appelle la mêmeis_allowed. Les valeursUser-agentvides sont désormais ignorées : un tel groupe ne nomme personne, et le groupe joker s’applique. Le groupe nommé le plus spécifique l’emporte toujours, et un robots.txt ne nommant personne continue de tout autoriser.CorrigéFonctionneCorrection de quatre défauts de sélection d’export et de validation de run qui laissaient
un audit présenter un résultat qu’il ne pouvait pas étayer (#209, #210, #215, #216).
internal_allne demandait que le jeton de nom de fichierinternal, si bien qu’un onglet Internal par type partiel (par ex.internal_html.csv, sans aucune ligne non HTML) satisfaisait l’export maître Internal:All requis sans avertissement de maître manquant. Le filtre exige désormais aussiall.discover_exportsprenait le premier candidat pour une clé logique dans l’ordre alphabétique des noms quand deux fichiers correspondaient (par ex.internal_all.csvetinternal-all.csvprésents avec des lignes différentes), en écartant silencieusement l’autre sans rien dans les métadonnées du run pour signaler qu’un choix avait été fait. Il lève désormais une erreur en nommant chaque candidat.run_sfchoisissait le sous-dossier horodaté le plus récent sous--out, quelle que soit sa date de création. Un relancement qui sortait avec le code 0 sans rien écrire (un échec au démarrage que Screaming Frog ne signale pas par un code de sortie non nul) pouvait donc renvoyer les exports d’une invocation antérieure comme s’ils étaient frais. Il compare désormais avec un instantané pris avant le démarrage du processus, et échoue bruyamment si rien de nouveau n’apparaît.build_commandn’ajoutait--auth-configque si le fichier de profil donné existait déjà. Il lançait donc silencieusement un crawl non authentifié sur un chemin mal tapé ou supprimé, ce qui est approprié pour leseospiderconfigoptionnel par défaut, mais faux pour un profil explicitement demandé par l’appelant. Unsf_cli.auth_configexplicite mais absent lève désormais une erreur avant que Screaming Frog ne démarre.CorrigéFonctionneCorrection de deux défauts d’extraction de contenu dans
seohead/checks/parser.py(#138,#140).
collapse_whitespacedécodait une seconde fois les entités HTML, en plus du décodage unique que le parseur lxml de BeautifulSoup effectue déjà sur chaque valeur quetag.get_text()/tag.get(attr)transmet à cette fonction. Sans effet sur un balisage ordinaire, cela transformait en revanche, sur une page dont le CMS ou la chaîne d’import avait déjà doublement échappé ses entités (un artefact réel et courant), une soupe d’entités visiblement cassée en texte d’apparence propre, et raccourcissait la longueur rapportée en dessous de ce qu’un onglet de navigateur ou un extrait SERP affiche réellement. Cela retournait dans les deux sens les contrôles de longueur des titres et descriptions. La fonction ne décode plus rien. Par ailleurs, l’extraction des liens et des sources d’URL parcourait tout le document sans condition. Un<a>ou un<img>existant seulement à l’intérieur d’un<template>inerte n’est jamais partie du document rendu selon la spécification HTML, et n’est jamais demandé par un navigateur ni par un crawler, à moins qu’un script ne le clone. Pourtant il était signalé comme un vrai lien sur la page, et réellement récupéré parspider.py. C’est le même mode d’échec d’URL fantôme que celui corrigé pour<base href>dans #4 ; les deux extracteurs ignorent désormais les descendants de<template>.<noscript>reste volontairement accessible ici, contrairement à l’extraction de texte : c’est un repli réel, défini par la spécification, qu’un client sans JavaScript (et la passe de crawl sans rendu des moteurs de recherche) charge bel et bien. L’exclure masquerait à l’auditeur une URL réellement récupérable. Le nombre de mots ne compte plus non plus le texte des descendants<svg>/<math>(le<title>d’accessibilité d’un sprite d’icônes, le<text>d’un glyphe, la notation MathML) comme contenu : un en-tête de 20 icônes pouvait doubler le nombre de mots rapporté par rapport au contenu réel. Cette liste d’exclusion vit désormais en un seul endroit,content_area.TEXT_EXCLUDED_TAGS, partagé par les deux extracteurs de texte, au lieu de deux copies qui avaient déjà divergé.CorrigéFonctionneConserver les jetons
relcomplets d’un lien, son attributtargetet son href brut(avant résolution), et extraire les éléments
<form>: méthode, action, présence d’un champ mot de passe (#125). Six problèmes catalogués dépendaient de la conservation de ces trois éléments, qui était jusqu’ici jetés : les liens cross-origin peu sûrs (target="_blank"sansrel="noopener"/"noreferrer"), les liens protocol-relative (//host/path, avant résolution), les liens sortants vers localhost, une page recevant à la fois un lien interne suivi et un lien nofollow, une action de formulaire non sécurisée, et un formulaire de mot de passe servi depuis une page HTTP en clair. Les six sont désormaisUNSAFE_CROSS_ORIGIN_LINK,PROTOCOL_RELATIVE_LINK,OUTLINK_TO_LOCALHOST,FOLLOW_AND_NOFOLLOW_INLINKS,FORM_URL_INSECUREetFORM_ON_HTTP_URLdans le registre. Les contrôles de formulaire, de localhost et de mélange nofollow n’ont besoin que de champs déjà enregistrés par chaque crawl : ces quatre-là s’exécutent sans condition. La paire cross-origin et protocol-relative demandelink_attributes.capture, désactivé par défaut, car les données supplémentaires par arête ont mesuré environ +50 % sur un crawl synthétique de 3 387 pages avec 150 liens par page, soit environ +95 octets par arête et +46 Mo au total,raw_hrefreprésentant l’essentiel. Le registre passe de 121 à 127 contrôles.CorrigéFonctionneCorrection de
report-buildqui rendait silencieusement un rapport sans aucun constatpour un audit SF Analyzer…
Correction de
report-buildqui rendait silencieusement un rapport sans aucun constat pour unaudit.jsonde SF Analyzer (#151). La recette documentée,sf run --tasksrouté versreport-build --format docx/xlsx/csv/md, lisait les clés platesfindingset les pages, que le schéma SF n’utilise pas : les constats sont sousissues, les faits de page souspages[].metrics. L’écart produisait un résumé0/0/0/0assuré pour un audit qui avait bel et bien trouvé des problèmes critiques, sans aucune section de constats.build_reportreconnaît désormais la forme nativeseohead.site-audit/1et la forme SF Analyzer, et normalise cette dernière en contrat plat que les quatre rédacteurs destinés aux lecteurs comprennent déjà, avant le rendu.--format jsonest inchangé : il relayait déjà correctement le document d’origine, ce qui prouvait que les données n’avaient jamais manqué. Un document qui ne correspond à aucun des deux contrats est refusé avecok: Falseen nommant l’écart de schéma, au lieu d’être rendu comme un livrable vide. La condition morteif ... : pass, qui ressemblait à une validation mais ne faisait jamais rien, a disparu.tests/test_docs_commands_execute.pyvérifie désormais le résumé rendu de la recette documentée contre les totaux propres deaudit.json, et pas seulement son code de sortie.ModifiéFonctionneFermeture de deux lacunes de sécurité financière dans
seohead/data_sources/(#157,#159).
geo_guardne contrôlait que la chaîne de pays consultativecountry.search_volume,keyword_ideas,keyword_difficultyetserppouvaient donc quand même atteindre le point d’accès en direct de DataForSEO pourlocation_code=2643(Russie) ou2112(Biélorussie) dès qu’un appelant fournissait le code géographique numérique sans renseignercountry. Le garde vérifie désormaislocation_codeen premier, puisque c’est le champ effectivement facturé. Par ailleurs, une exception réseau (URLError/TimeoutError/SSLError) pendant un appel facturé relançait la charge utile identique sans clé d’idempotence. C’était le cas dupostde DataForSEO, du/setd’Arsenkin, et dewordstat.topRequests/wordstat.dynamics/web.searchAsyncde Yandex Cloud, qui ne journalisaient que la tentative finissant par renvoyer une réponse. Aucun des trois fournisseurs n’offre de mécanisme d’idempotence pour ces points d’accès. Une réponse perdue n’est donc plus relancée : la tentative est enregistrée dans le registre des dépenses (coût inconnu, marquéattempt_failed: network_error) et l’appel échoue purement et simplement, au lieu de risquer un second débit. Les lectures idempotentes (/checket/getd’Arsenkin, interrogation des opérations Yandex Cloud) ne sont pas touchées et continuent de relancer.yandex_cloud.WebSearch.search_batchisole désormais une soumission perdue à sa propre requête au lieu d’abandonner le lot.metrika.pyrelance les erreurs réseau de la même façon, mais ce n’est pas un coût monétaire et il n’a pas été modifié. À reprendre si la création de son export Logs API doit recevoir le même traitement pour des raisons de quota.ModifiéFonctionneFermeture d’une faille de DNS rebinding dans
http_client()(#142) :pinned_target(),correctif de la fenêtre TOCTOU de #14…
Fermeture d’une faille de DNS rebinding dans
http_client()(#142) :pinned_target(), correctif de la fenêtre TOCTOU décrite dans #14, ne protégeait que la récupération en mode liste decollect.py. Les quatorze autres points d’appel, y compris le moteurcrawl-sitedespider.py, laissaient httpx résoudre le DNS une seconde fois, indépendamment, pour ouvrir le socket. Un résolveur hostile répondant différemment à cette seconde requête pouvait donc atteindre169.254.169.254ou une adresse RFC 1918, quel que soit le verdict du garde.http_client()construit désormais chaque client sur un transport qui épingle la connexion à l’adresse qu’il résout lui-même, à la première requête et à chaque saut de redirection, en ne gardant le nom d’hôte que pour l’en-têteHostet le SNI TLS. La correction est donc structurelle, et non une discipline que chaque appelant devait retenir.SEOHEAD_ALLOW_PRIVATE_NETWORKS/SEOHEAD_ALLOW_PRIVATE_HOSTSrestent inchangés.CorrigéFonctionneCorrection du contrat de code de sortie CLI/MCP pour un échec signalé par un handler
lui-même, et complétion de
SOURCE_FLAGS(#155, #156). Un handler renvoyant{"ok": false, ...}, la façon documentée qu’a la couche outils de signaler un échec de récupération, d’analyse ou de fournisseur sans lever d’exception, affichait ce JSON et sortait avec le code 0 en CLI. Via MCP, il renvoyait un résultat normal, sansisError. Un pipeline testant$?ou un client ne vérifiant queisErrorne pouvaient donc pas détecter l’échec.cli.pysort désormais avec le code 1 dans ce cas. Le code 2 delog-scanpour un run qui se contredit reste un signal distinct et documenté (voirdocs/USAGE.md).mcp_server.pylèveToolErrordepuis un wrapper partagé_checked(), de sorte qu’un client voitisError. Les deux appellent une mêmehandlers.handler_failed(), pour que les deux interfaces ne divergent pas sur ce qui compte comme un échec. Par ailleurs,SOURCE_FLAGSa gagné--phrase,--keywords,--query/--queries,--seed,--counter, et--before/--after. Chacun identifie déjà l’entrée complète d’une commande, comme le fait--url, mais il manquait : une boucle ligne par ligne sur l’un d’eux ne traitait donc silencieusement que sa première ligne. L’ensemble est désormais construit par_source_flag()au moment où chaque drapeau est déclaré, au lieu d’être listé à la main à part, ce qui l’empêche de diverger à nouveau.ModifiéFonctionneExtension de
docs/scenarios/de dix enchaînements à cinquante-six, regroupés par laquestion que se pose le lecteur à son arrivée (#120). Les scénarios retenus sont fixés par la carte de couverture, et non par goût : chacun déclare les problèmes catalogués qu’il résout.
tests/test_scenario_coverage.pyvérifie que chaque problème que cet outil prétend trouver figure dans au moins un scénario, qu’aucun scénario ne cite un problème absent de la carte, et qu’aucun n’omet ses limites. Les 143 problèmes repérables sont tous couverts. Chaque commande de chaque fichier est toujours exécutée sur le site de test en CI. La suite compte 2 008 tests, dont la garde des commandes de documentation est désormais le plus gros groupe.ModifiéFonctionneVérification des limites propres au protocole sitemap (#124) :
SITEMAP_TOO_MANY_URLSau-delà de 50 000 entrées dans un seul fichier,
SITEMAP_TOO_LARGEau-delà de 50 Mo non compressés, etSITEMAP_URL_DUPLICATEDpour une URL déclarée dans plus d’un sitemap, en nommant les deux documents. Un fichier qui dépasse l’une de ces limites est invalide, et non simplement volumineux : un moteur de recherche peut en lire une partie et écarter le reste sans que le propriétaire du site le voie. Les seuils viennent du protocole, non de la configuration. Les deux parseurs enregistrent désormais la taille en octets et le nombre déclaré de chaque document, et les constats nomment le sitemap enfant plutôt que l’index. Le registre passe de 118 à 121 contrôles.AjoutéFonctionneAjout de
docs/GUIDELINE.md, le document à lire en premier (#121) : ce que c’est, ce quece n’est pas…
Ajout de
docs/GUIDELINE.md, le document à lire en premier (#121) : ce que c’est et ce que ce n’est pas, une première exécution de bout en bout, comment choisir un débit de crawl et comment savoir si les erreurs viennent de vous, comment lireaudit.jsonsans s’y laisser induire, ce que le fichier de configuration modifie et ce qu’il ne fait que coûter, les six erreurs que tout le monde commet au début, quoi faire quand l’outil se trompe, et ce à quoi il ne peut pas répondre du tout. Il est lié en premier depuis le README, avant la référence.AjoutéFonctionneAjout de
docs/COVERAGE_SF_ISSUES.md, généré à partir deseohead/sf/core/sf_issue_map.py: chacun des 320 problèmes du catalogue publié du domaine, avec un statut unique. Ce statut est soit « trouvé par un contrôle nommé », soit « trouvé par une commande nommée », soit « trouvé en partie, avec la part manquante indiquée », soit une lacune nommée, soit une décision motivée (#119). Sur les 212 problèmes dans le périmètre, 126 sont trouvés aujourd’hui et 17 en partie ; 41 sont des lacunes et 28 nécessitent quelque chose délibérément non construit. L’accessibilité (92) et AMP (16) sont écartées par décision unique, avec leurs listes complètes affichées pour que la décision soit vérifiable. Un test vérifie que chaque identifiant de contrôle et chaque commande référencés existent encore : un renommage casse donc le build au lieu de laisser le document périmer. Remplacedocs/CHECKLIST_AUDIT.md, rédigé sans le catalogue sous les yeux.ModifiéFonctionneLe seeder de sitemap demande l’adresse publiée par le sitemap, et non sa forme normalisée
(#115).
sitemap.crawl()ne renvoyait que lelocnormalisé : un sitemap déclarant/a/provoquait la récupération de/a. Sur la plupart des CMS, c’était une redirection 301 que le crawler inventait puis rapportait comme un fait sur le site, et un 404 partout où la forme sans slash n’est pas routée. Chaque entrée porte désormaisloctel que publié, accompagné deloc_normalizedpour les consommateurs qui comparent sur celui-ci. Les statistiques de redirection des crawls précédents amorcés par sitemap sont suspectes pour cette raison.ModifiéFonctionneRestructuration de la skill opérateur en répertoire contrôleur (#111).
.claude/skills/comporte désormais deux niveaux : 21 skills de méthode, chacune couvrant une chose bien, etcontrol/, qui décide lesquelles lancer sur un site que personne n’a encore examiné. Le contrôleur route au lieu de reformuler. Il contient cinq sous-skills chargeables (cadrage, débit et charge, lecture d’un audit, vérification, livrables) ainsi qu’une archive de référence en trois fichiers : défauts trouvés sur des sites réels et ce qui a permis de les repérer, population que décrit chaque contrôle, et ce à quoi l’outil ne peut pas répondre du tout. La garde de langue anglaise seule et la garde des commandes de documentation couvrent désormais chaque fichier Markdown sous un répertoire de skill, et plus seulementSKILL.md.AjoutéFonctionneAjout de
tests/chains/: un site de test construit à partir des formes qui cassentréellement les enchaînements —…
Ajout de
tests/chains/: un site de test construit à partir des formes qui cassent réellement les enchaînements (les deux formes avec et sans slash final d’une même URL, un corps qui n’est pas de l’UTF-8 valide, une page en windows-1251, un bandeau hors de<main>, un lien vers un autre hôte, un chemin interdit par robots.txt). Il est exploré en loopback, avec dix-sept assertions portant sur l’exécution dans son ensemble plutôt que sur un module donné (#112). Quatre propriétés sont testées : conservation (un nombre ne change pas de sens en traversant le pipeline), population (un constat porte sur un membre de l’ensemble qu’il décrit), déterminisme (deux crawls, deux niveaux de concurrence, une seule réponse) et représentation (une page indique comment elle a été mesurée). Les règles de population sont celles delogscanlui-même : une contradiction que le scanner sait nommer est une assertion de ce test d’enchaînement.ModifiéFonctionne
reconcile_sitemapsignale chaque URL telle qu’elle a été écrite, et non telle qu’elle aété normalisée.
La comparaison se fait toujours sur la clé normalisée. Mais un constat qui nommait une forme normalisée désignait une URL n’apparaissant nulle part dans le crawl : inexploitable pour un lecteur, et indiscernable, pour le scanner d’anomalies, d’un constat sur une page jamais récupérée.
AjoutéFonctionneAjout de
docs/scenarios/: dix enchaînements de bout en bout, chacun avec les vraiescommandes dans l’ordre, l’artefact produit, son coût, et ce que cet enchaînement ne peut pas répondre (#110). Le reste de la documentation liste ce que l’outil contient ; celle-ci décrit ce qu’il fait. Lien depuis le README, au-dessus de la liste des outils, pour qu’un agent qui évalue le dépôt trouve les enchaînements avant l’inventaire. Chaque commande présentée est exécutée sur le site de test par
tests/test_docs_commands_execute.py, dont l’extracteur et les gardes de langue anglaise seule et de comptage parcourent désormaisdocs/à tous les niveaux, et plus seulement son premier niveau.AjoutéFonctionneAjout de
log-scan(CLI) et deseo_log_scan(MCP) : lire les artefacts d’un runterminé et signaler les affirmations incompatibles entre elles…
Ajout de
log-scan(CLI) et deseo_log_scan(MCP) : lire les artefacts propres d’un run terminé et signaler les affirmations qui ne peuvent pas être toutes vraies à la fois (#109). Huit règles, chacune écrite à partir d’un défaut livré malgré toute la suite de tests : une taille enregistrée en désaccord avec le fichier sur le disque, un ratio de texte supérieur à 100 %, un contrôle qui se déclenche plus souvent qu’il n’y a de pages sur lesquelles le faire, un constat sur une URL que le run n’a jamais récupérée, une canonical traitée comme une redirection alors que cette URL répondait 2xx dans le même run, un résumé en désaccord avec ses propres lignes, des mots comptés sur une page de zéro octet, et des pages mesurées de deux façons où seules certaines précisent laquelle. Chaque anomalie nomme les deux valeurs et l’endroit où chacune a été lue. La CLI sort avec le code 2 lorsqu’un run se contredit, afin qu’un pipeline s’arrête au lieu de publier les chiffres.ModifiéFonctionneLe test de cadencement entre workers ne mesure plus l’horloge murale (#107).
_DispatchGatelit désormais l’horloge injectée du crawl au lieu detime.monotonic()directement. Le test le pilote avec une horloge virtuelle qui n’avance que lorsque quelque chose dort, de sorte que les instants d’envoi sont l’arithmétique du crawler lui-même et que l’assertion est exacte. L’ancienne forme comparait les écarts réels écoulés à un plancher de 0,024 s, et échouait sur un code inchangé chaque fois que la machine était occupée.ModifiéFonctionneFermeture de la seconde moitié de l’audit des réglages non branchés (#91).
http.headersest fusionné dans chaque requête à côté des en-têtes d’identifiants.speed.adaptivecontrôle le délai du throttle et l’ajustement de la concurrence ; les compteurs de timeout et d’erreurs serveur continuent de tourner, car abandonner est un mécanisme distinct du ralentissement.discovery.hyperlinks.store/.crawl,discovery.external.storeetdiscovery.redirects.crawldéterminent désormais ce que le crawl enregistre et ce qu’il suit.discovery.canonicals.*,discovery.external.crawletdiscovery.redirects.storesont supprimés au lieu d’être câblés : ils désignaient une capacité que le spider n’a pas (suivre les canonical, explorer d’autres hôtes) ou un état qu’il ne peut pas retenir. Un réglage qui apparaît dans--config-helpet dans le manifeste de run sans rien changer vaut moins que l’absence de réglage. La liste d’exemptions du témoin de couverture est désormais vide : chaque chemin deDEFAULTSmodifie un résultat observable et est nommé par un test.ModifiéFonctionneDétecter la zone de contenu à partir de la sémantique du document quand rien n'est
configuré (#96) :
main, puis[role="main"], puisarticle, en enregistrant lequel a correspondu sousauto_main/auto_role_main/auto_article. L'ancien défaut, à savoir tout le body moins les balisesnavetfooter, comptait 126 mots de gabarit sur 433 dans un article WordPress réel (29 %), dont un lien d'accès rapide au contenu. Cette surestimation alimentaitTHIN_CONTENTetLOW_TEXT_RATIOdans le même sens sur chaque page d'un gabarit.headeretasiderejoignentnavetfooterdansDEFAULT_EXCLUDE_TAGSpour le chemin de repli. Un sélecteur configuré reste prioritaire, et un sélecteur qui ne correspond à rien revient toujours àfallback_default_bodyau lieu de détecter silencieusement une autre zone.ModifiéFonctionne
URL_NOT_IN_SITEMAPcompare désormais des pages avec des pages (#94).Il comparait les URL d'un sitemap à chaque destination du graphe de liens du crawl. Sur un site réel de 124 pages, la règle se déclenchait donc 392 fois : 362 fichiers image qu'une galerie lie directement, cinq liens vers d'autres hôtes et 30 URL que le crawl n'avait jamais récupérées. Cela représentait 74 % de ce rapport et masquait les constats réels. Le côté observé ne contient plus que les pages qu'un sitemap est censé déclarer : récupérées, en 2xx, au format HTML, sur le même hôte et indexables.
reconcile_sitemapreçoit cette population dans un argumentcomparabledistinct.SITEMAP_ORPHANcontinue donc de vérifier l'accessibilité par rapport à toutes les destinations de liens, sans pouvoir inventer d'orphelines. Ce qui a été mis de côté est renvoyé souslinked_not_comparableau lieu d'être supprimé.CorrigéFonctionneCorriger les contrôles canonical sur un site qui sert les deux formes d'une URL, avec et
sans slash final (#95).
norm_urlsupprime volontairement le slash final, donc une balise canonical écrite sans slash correspond à la page qui en a un. Mais l'index normalisé ne gardait qu'une page par clé, alors qu'un crawl de site WordPress type en contient deux :/x(301) et/x/(200). Lire celle qui avait été insérée en premier faisait signaler àCANONICAL_TO_REDIRECT78 pages actives dont la canonical répond 200.AuditContextexpose désormaispages_by_norm, qui range toutes les pages sous une même clé, etpage_by_normrenvoie la variante qui a répondu en 2xx.CANONICAL_TO_REDIRECTetCANONICAL_NON_INDEXABLEne se déclenchent que si aucune variante ne les contredit.CorrigéFonctionneCorriger
size_bytes: il correspond désormais au corps de la réponse tel qu'il estarrivé sur le réseau, mesuré avant décodage (#99). Il était mesuré sur la chaîne décodée, si bien que chaque octet non valide en UTF-8 devenait U+FFFD et était réencodé sur trois octets. Un WebP de 739 Ko issu d'un crawl réel était enregistré à 1,27 Mo, et le facteur de gonflement variait d'un fichier à l'autre. Les images, PDF, polices et HTML servis dans un jeu de caractères historique (windows-1251) étaient tous surestimés, de même que le ratio de texte calculé sur ce dénominateur. Le cache HTTP stocke désormais la taille réseau avec l'entrée, donc une page rejouée affiche la taille relevée lors de la récupération en direct. Son schéma passe à
http_cache.v2, et les entrées v1 sont récupérées à nouveau une fois au lieu d'être rejouées sans taille.AjoutéFonctionneAjouter
docs/TOOL_REFERENCE.md, généré à partir des définitions d'outils MCP(
seohead/mcp/tool_reference.py,scripts/generate_tool_reference.py) : pour chaque outilseo_*/sf_*, ses arguments avec leur type et leur valeur par défaut, son coût (réseau, écritures, idempotence, dépense, lu dans son profilToolAnnotations) et les notes de comportement et de modes d'échec de sa propre docstring.tests/test_docs_drift.pyfait échouer le build s'il s'écarte des définitions d'outils ou s'il manque un outil.AjoutéFonctionneAjouter
tests/test_docs_commands_execute.py: il extrait chaque commandeseohead ...présentée dans le README, la documentation, les skills et les exemples (
scripts/doc_commands.py), puis exécute chacune hors ligne, sur un site de test en boucle locale (tests/doc_fixtures/) et sur des copies matérialisées deexamples/, en vérifiant une sortie propre. Les commandes qui exigent une vraie infrastructure (RDAP/DNS, binaire SF sous licence, identifiants d'un fournisseur payant, serveurmcpqui ne se termine jamais) sont au moins analysées par le parseur d'arguments réel. Une commande documentée qui ne fonctionne plus fait désormais échouer la CI au lieu d'être livrée obsolète.ModifiéFonctionneRefondre les 21 skills de workflow techniques (
.claude/skills/*/SKILL.md) selon unestructure commune : Déclencheur, Anti-déclencheur, Prérequis (sous forme de checklist), le Workflow existant, Points de décision, Définition de terminé (sous forme de checklist) et Coût. Corriger le nombre de commandes obsolète en bas de
docs/SKILLS.md: le nombre réel, recalculé en parcourant chaque fichier de skill, est désormais vérifié par un nouveau test de dérive.CorrigéFonctionneCorriger plusieurs compteurs d'outils et de tests qui avaient silencieusement dérivé des
registres réels (un nombre de commandes CLI obsolète…
Corriger plusieurs compteurs d'outils et de tests qui avaient silencieusement dérivé des registres réels : nombre de commandes CLI obsolète dans
docs/USAGE.md,docs/COMPARISON.md,README.mdetdocs/TESTING.md, nombre d'outils MCP obsolète dansdocs/TOOLS.md, nombre de références d'outils obsolète dansdocs/README.md, et nombre de tests hors ligne. Les valeurs corrigées sont figées par un test de régression (test_stale_tool_counts_do_not_reappear).AjoutéFonctionneAjouter les quatre audits Lighthouse statiques qui n'ont besoin ni de navigateur ni d'API
tierce (#59) :
MISSING_CHARSET,MISSING_DOCTYPE,VIEWPORT_MISSINGetNO_COMPRESSION, chacun calculé à partir des éléments qu'un crawl détient déjà.content_encoding,meta_charset,doctypeetviewportrejoignent le vocabulaire de colonnes normalisées, de sorte qu'un export Screaming Frog qui porte ces valeurs en colonnes d'extraction personnalisée alimente les mêmes contrôles. Le registre passe de 114 à 118 contrôles.ModifiéFonctionneBrancher les dix réglages restants du crawler, validés et écrits dans le manifeste
d'exécution, mais décrits par
--config-helpet lus par personne (#63).limits.max_response_bytes,speed.max_delay_seconds,robots.user_agent_tokenetspeed.stop_after_consecutive_timeoutspilotent désormais un comportement qui était codé en dur.robots.unavailable_means_stopdétermine si un robots.txt injoignable ou en 5xx arrête le crawl ou est traité comme sans restriction. Auparavant, un robots.txt injoignable n'arrêtait jamais le crawl, quelle que soit la politique, alors qu'un robots.txt en 5xx l'arrêtait toujours. Les deux cas forment désormais un seul cas « indisponible », régi par ce réglage.limits.max_url_length,limits.max_query_variants_per_path,http.retry_on_timeoutetdiscovery.follow_nofollowsont des comportements nouvellement implémentés.http.user_agentest appliqué aux vraies requêtes, au lieu d'envoyer toujours l'user-agent par défaut du toolkit. Ajout decrawl-describe-settings(CLI) etseo_crawl_describe_settings(MCP), pour qu'un agent découvre la surface de configuration sans accès au système de fichiers (#23).AjoutéFonctionneAjouter la recherche personnalisée (
tools/custom_search.py) et l'extractionpersonnalisée (
tools/custom_extract.py) sur un corpus déjà crawlé : filtres de présence et d'absence (source brute, texte visible, élément CSS nommé ou nœud XPath), ainsi que des extracteurs CSS, XPath et regex. Les deux indiquent sur quelle représentation ils ont travaillé, balisage statique ou DOM rendu. L'absence est comptée honnêtement : une page dont la récupération a échoué est exclue du numérateur comme du dénominateur, au lieu d'être comptée comme manquante. L'extraction exécute chaque couple (document, extracteur) sous un budget de temps réel (SIGALRMsur POSIX) : une expression pathologique n'interrompt que ce document, et l'exécution se termine quand même.AjoutéFonctionneAjouter la classification de la position des liens (
tools/link_position.py) :navigation, en-tête, barre latérale, pied de page ou contenu, par règles ordonnées sur le chemin des ancêtres d'un lien. Elle réutilise la notion de contenu de
content_area.pyau lieu d'en inventer une seconde. Branchée sur l'enregistrement des liens decrawl/spider.pyderrièrelink_position.classify(désactivée par défaut, car une position par lien coûte de la mémoire sur un gros crawl), elle est agrégée à l'échelle du site parinlink_compositiondecrawl/linkgraph.py. Cette agrégation alimente un nouveau constatINLINK_BOILERPLATE_ONLY, pour les pages liées uniquement depuis le gabarit. Le registre passe de 104 à 105 contrôles.AjoutéFonctionneAjouter huit calculs de second passage après le crawl, possibles seulement une fois
celui-ci terminé (issue #15). Un score de maillage interne calculé à partir du graphe d'arêtes
all_inlinks(LOW_LINK_SCORE) ; une cible canonical vers laquelle aucun lien hypertexte ne pointe (UNLINKED_CANONICAL) ; la détection des bouclesrel="next"et des séries non liées (PAGINATION_LOOP,UNLINKED_PAGINATION_SERIES) ; la réciprocité hreflang (HREFLANG_MISSING_RETURN_LINK) ; les agrégats de composition des liens entrants (ONLY_NOFOLLOW_INLINKS,ONLY_NONINDEXABLE_SOURCE_INLINKS) ; le chemin de découverte le plus court, concret, depuis le seed du crawl (DEEP_DISCOVERY_PATH) ; un repli calculé en interne pour le contenu mixte (INSECURE_SUBRESOURCE) ; et le regroupement des quasi-doublons à partir du texte de page stocké (NEAR_DUPLICATE/DUPLICATE_BY_HASH), avec le branchement detools/duplicate.pyettools/content_area.pydans l'audit pour la première fois.ORPHAN_PAGE,SITEMAP_ORPHANet les deux nouveaux contrôles « non liés » sont désormais retenus : signalés comme un contrôle ignoré nommé, et non comme un constat, sur un crawl que l'agrégateur a marqué comme partiel. « Rien ne pointe vers cette page » ne se prouve pas sur un crawl tronqué. Le registre passe de 104 à 114 contrôles.ModifiéFonctionneAuditer le registre des contrôles du crawl face à une checklist SEO technique externe et
combler huit lacunes peu coûteuses et vérifiées : cinq contrôles hreflang (codes de langue ou de région invalides, auto-référence absente, x-default manquant, entrées en double, cibles non canoniques), deux directives robots (
notranslate,unavailable_after) et les URL canonical contenant un fragment. Le registre passe de 96 à 104 contrôles. Voirdocs/CHECKLIST_AUDIT.md.AjoutéFonctionneAjouter
asset-weight-check: il récupère les CSS et JS liés à une page et signale lesressources bloquant le rendu, les fichiers trop lourds, les bibliothèques en double (par hash de contenu), l'absence de minification, l'absence de
font-display, le JS legacy avec polyfills, ainsi que l'absence de compression ou de mise en cache longue durée.AjoutéFonctionneAjouter
crawl-site --sitemap <url>(etsitemaps.auto_discoverdans--config) :amorcer le crawler natif à partir des URL déclarées dans un sitemap, suivre les liens de chacune, puis rapprocher les ensembles déclaré et observé dans le bloc
summary.sitemapdeaudit.json. Le rapprochement utilise les mêmes identifiants de contrôleSITEMAP_ORPHAN/URL_NOT_IN_SITEMAPque ceux déjà remontés par le pipeline Screaming Frog.AjoutéFonctionneAjouter
crawl-site --config-help, généré depuisseohead/crawl/config.py, et masquerdeux options de profondeur et de délai…
Ajouter
crawl-site --config-help, généré depuisseohead/crawl/config.py, et masquer--max-depthet--min-delaydans--help(toujours acceptés), afin que la surface des options cesse de grossir à chaque réglage du crawler.ModifiéFonctionneSéparer la détection des technologies en une étape de récupération et une étape pure
analyze_tech, capturer les identifiants analytics et des gestionnaires de balises au lieu des seuls noms, et ajouter un rapporttag_coveragequi regroupe la présence par gabarit d'URL et indique comment chaque page a été mesurée.ModifiéFonctionneRésoudre les chaînes et boucles de redirections en second passage sur les cibles de
redirection d'un crawl terminé…
Résoudre les chaînes et boucles de redirections en second passage, sur les cibles de redirection propres à un crawl terminé.
REDIRECT_CHAIN/REDIRECT_LOOPn'exigent plus le rapport natif Screaming Frog Redirect Chains : un export en profil léger ou une exécution decrawl-siteobtient les mêmes constats, sans effort supplémentaire.AjoutéFonctionneAjouter une zone de contenu configurable (
content_area.py) qui limite le comptage demots à la zone principale, en excluant par défaut la navigation et le pied de page, sans toucher à la découverte des liens. La stratégie retenue est indiquée pour chaque page.
ModifiéFonctionneSéparer les doublons exacts des quasi-doublons dans
duplicate.py: les correspondancesexactes sont hachées à partir du texte extrait (et non des octets bruts) et exclues des groupes de quasi-doublons, et les comparaisons ne portent par défaut que sur les pages indexables.
AjoutéFonctionneAjouter un rapport de cohérence des gabarits (
boilerplate_report.py) qui hachel'en-tête, la navigation et le pied de page de chaque page, et signale les groupes de gabarits minoritaires.
AjoutéFonctionneAjouter l'extraction Markdown sans dépendance (
markdown_extract.py) : un rendu limité àla zone de contenu et un rendu du document complet.
ModifiéFonctionneBrancher
markdown_extractetboilerplate_reportdans la surface CLI et MCP, sous lesnoms
markdown-extract/seo_markdown_extractetboilerplate-report/seo_boilerplate_report(47 outils de base, contre 45 auparavant). Ajout du drapeauonly_indexablequeduplicate_checkavait déjà au niveau du handler, à la signature MCP deseo_duplicate_check, où il avait été oublié.citability-checkne lit plus le champtextdu parseur sur tout le document (une seule ligne condensée, sans aucune rupture de paragraphe ni de titre) : il s'appuie désormais sur le Markdown de la zone de contenu produit parmarkdown_extract. Cela corrige à la fois la dilution par le gabarit soulevée dans l'issue et un bug latent où ce texte à plat annulait silencieusement les dimensions Answer-Blocks et Structure-Quality pour toutes les URL en direct. Volontairement non limités : le champtextdu parseur lui-même, qui reste sur tout le document, car l'extraction des preuves de schéma depage_facts.py(liens sociauxsameAs, fil d'Ariane, expressions régulières de prix et de notes) dépend de faits qui vivent légitimement dans les widgets d'en-tête et de pied de page, exclus de la zone de contenu ; et les contrôlesTHIN_CONTENT/LOW_TEXT_RATIOpilotés par Screaming Frog danssf/core/rules.py, dontword_countettext_ratioproviennent des colonnes d'export de Screaming Frog. Ce sont des données tierces que le toolkit ne peut recadrer sans re-récupérer chaque page, ce qui irait à l'encontre de la conception hors ligne sans requête du chemin d'audit SF. Le crawler du toolkit (crawl-site) bénéficie déjà gratuitement du cadrage sur la zone de contenu, puisque son comptage de mots lit directementparser.parse_html.ModifiéFonctionneRédiger une convention de nommage (
docs/NAMING.md) et résoudre les collisions de nomsde base de modules, ainsi que les fichiers de test nommés d'après un processus, relevés au passage. Aucun nom de commande CLI, de handler ni d'outil MCP n'a changé.
AjoutéFonctionneAjouter les fichiers communautaires, de citation et d'onboarding des agents, sans clé
d'API.
AjoutéFonctionneAjouter le skill de workflow
analytics-console-review, soumis à autorisation, et troisrecettes pratiques.
- ModifiéFonctionneDocumenter la prise en charge de la ligne de sécurité actuelle
3.x. ModifiéFonctionneExiger TLS 1.2 ou plus récent et épingler les sondes de certificat directes aux adresses
publiques préalablement validées.
AjoutéFonctionneAjouter un cache de réponses HTTP pour
crawl-site(seohead/crawl/cache.py, activé paroption…
Ajouter un cache de réponses HTTP pour
crawl-site(seohead/crawl/cache.py, activé viacache.mode, valeur par défautoff, pour qu'aucun effet de bord n'apparaisse derrière un réglage par défaut). Il offre une vraie gestion de la fraîcheur HTTP (max-age/Expires, revalidationETag/Last-Modified, variantes tenant compte deVary, respect deno-store/no-cache), un modereplaydestiné au débogage, inscrit dans le manifeste et jamais activé par défaut, et un drapeauinvalidatepour un rafraîchissement forcé explicite. Chaque page récupérée portecache_status(hit/revalidated/miss). L'exécution portecache_statsetcache_replay, à la fois dans la sortie du handler et dans le blocrundeaudit.json, si bien qu'un rapport construit en partie depuis le cache le signale. Un hit de cache ne coûte aucune requête et ne consomme ni délai de throttle ni emplacement de la file de dispatch concurrente : l'attente est déclenchée depuisfetch_onelui-même, uniquement lorsqu'un aller-retour réseau réel est sur le point d'avoir lieu.AjoutéFonctionneAjouter la réutilisation pilotée par le journal à
seohead/core/runlog.py(
SEOHEAD_REUSE_POLICY, un âge maximal en secondes par outil ; vide par défaut, ce qui signifie que rien n'est jamais réutilisé). Une réponse antérieure réussie et encore fraîche est renvoyée au lieu de rappeler l'outil. Elle est marquéereused: trueavecreused_from_ts, à la fois dans le résultat et dans la nouvelle entrée de journal qui est toujours écrite. La fraîcheur se mesure toujours à partir du moment où la valeur a réellement été récupérée, et la réutilisation ne la prolonge jamais.
3.0.0
Première version publique30 août 202610 entrées · Première version publique10 modifié
30 août 2026
10 modifié- ModifiéFonctionneUn seul paquet Python avec 42 outils CLI/MCP partagés.
- ModifiéFonctionneCinq outils MCP Screaming Frog supplémentaires et un analyseur de crawl à 96 contrôles.
ModifiéFonctionneReconnaissance du domaine, du CDN, des technologies, de la sécurité, des miroirs, des
régions, des bots et des backlinks.
- ModifiéFonctionneValidation Schema.org et génération de graphes connectés.
ModifiéFonctionnePreuves de site bornées à partir du sitemap, et rapports XLSX, DOCX, CSV, Markdown et
JSON.
- ModifiéFonctionneIntégrations optionnelles Wordstat, SERP Yandex, Arsenkin, Metrika et DataForSEO.
- ModifiéFonctionneVingt et un skills de workflow techniques et sept playbooks SEO empaquetés.
ModifiéFonctionneMCP stdio local et prise en charge de Docker ; ni interface graphique, ni API hébergée,
ni télémétrie.
ModifiéFonctionneREADME public fondé sur des preuves, avec des visuels reproductibles d'audit, de tâches,
d'interface et de rapports…
README public fondé sur des preuves, avec des visuels reproductibles (audit synthétique, tâches, interface et rapports).
- ModifiéFonctionneFrontière de publication sans historique et notices explicites des tiers.
Date = le jour où le changement a atterri (date git de son fragment changelog.d/). Numéro = l'issue qu'il clôture. Titre = première phrase, détails au clic.