# Méthode de concordance — 1.0.0

Application / moteur 1.0.0 · schéma de profil 1.0 · documentation 2026-10-04.

## Contrat du contrôle

Quatre questions distinctes : syntaxe JSON ; lecture des structures par ce profil ; concordance des valeurs ; exactitude éditoriale. Le moteur traite les trois premières dans son périmètre. Un journal de révision indépendant reste nécessaire pour la quatrième. Ni validation Schema.org complète, ni mesure d’effet SEO/GEO, ni test de résultats enrichis Google.

La configuration rend les attentes explicites. Les règles de ce dépôt (unicité d’une sélection, positions contiguës du fil, formats de dates, marqueur de gabarit) sont des **règles de projet**, pas des obligations universelles de Google ou Schema.org.

## Un document lu comme une donnée

parse5 construit un arbre de données. Aucun DOM importé, iframe, srcdoc, CSS, image, formulaire ou script n’est inséré ou exécuté. Les preuves sont du texte échappé. Même le HTML contenu dans `acceptedAnswer.text` passe par ce parseur. Les ressources tierces ne sont pas chargées. La CSP ajoute une défense ; la recette réseau vérifie séparément le comportement.

Les fichiers sont décodés en UTF-8. Les encodages anciens ne sont pas convertis automatiquement. L’extraction de texte n’établit pas la visibilité, l’accessibilité ou l’état visuel d’un élément : un `details` fermé contient encore sa réponse.

Le rapport conserve `html_source` ou `dom_snapshot`, nom, URL de référence déclarée/extrait déclaré par le profil, capture facultative et provenance. Une capture manuelle reste une déclaration. Si le fichier source ne contient aucun script LD, le rapport indique que la présence après exécution JavaScript n’est pas vérifiée. Il ne conclut pas à l’absence de données sur le site.

## Lecteur JSON-LD compact

Formes acceptées : objets, tableaux racine dont les objets déclarent leur contexte, `@graph`, objets imbriqués, types simples ou tableaux de types compacts, références `@id`. Contextes exacts embarqués comme identifiants : `https://schema.org`, `https://schema.org/`, `http://schema.org`, `http://schema.org/`. **Aucun contexte n’est téléchargé.**

Les contextes objets/tableaux/personnalisés, préfixes, IRI longues de propriété, `@list`, `@set`, `@value`, `@reverse`, cadres JSON-LD et autres mots-clés ne sont pas implémentés. Ils donnent `non_pris_en_charge` au niveau du script. Les propriétés ordinaires hors des lecteurs ne sont pas contrôlées ; le rapport n’établit pas leur validité dans le vocabulaire. Les entités visées sont Article/BlogPosting/NewsArticle, Person/Organization, BreadcrumbList/ListItem, FAQPage/Question/Answer.

Les scripts actifs `application/ld+json` sont distingués des exemples en `pre`, `code`, attributs et `template`. Un script invalide est signalé avec son index ; les autres sont examinés et la page garde une couverture partielle. Les clés dupliquées sont détectées avant la conversion en objet. Les clés susceptibles d’altérer un prototype sont rejetées par la politique du lecteur.

Les descriptions d’un même `@id` sont regroupées dans le fichier. Des déclarations strictement identiques d’une propriété se regroupent ; les éléments répétés d’un tableau sont conservés afin de ne pas masquer une clé FAQ ou une position dupliquée. Des valeurs concurrentes pour une propriété unique deviennent non déterminées. La V1 peut donc demander une revue prudente sur des graphes légitimes mais plus riches que le profil.

Les références s’examinent localement, sans expansion récursive arbitraire : la lecture bornée des propriétés ne suit pas les relations non utilisées. Un cycle n’est pas, en soi, une erreur. Une référence externe sans description locale reste « non résolue dans ce périmètre ». Il n’existe **pas de résolution entre fichiers du lot** : chaque page est une observation autonome.

## Sélection et base des URL

L’article se sélectionne par types, puis soit par relation `url` / `mainEntityOfPage` à l’URL déclarée, soit par `@id` choisi, soit par candidat unique. Un article recommandé ne devient pas implicitement l’article principal. Zéro ou plusieurs candidats restent non déterminés.

Base de résolution : URL déclarée par l’import, ou URL choisie explicitement par le sélecteur `pageUrl` du profil ; elles doivent concorder si les deux sont fournies. Un unique `<base href>` peut ensuite définir la base de résolution des valeurs du document. Plusieurs bases sont ambiguës. Une URL absolue ne nécessite pas de base ; une relative sans base reste non déterminée. L’URL de GitHub Pages n’intervient jamais.

Le profil de démonstration choisit explicitement `link[rel="canonical"]` comme URL de page. Ce choix configurable n’établit pas que canonical, identité `@id` et destination de navigation sont interchangeables. Les identifiants de nœuds vides `_:` peuvent se résoudre localement ; ils ne sont jamais des destinations de lien.

Normalisation URL : parseur WHATWG `URL`, résolution des relatives, normalisation standard de l’hôte et des ports par défaut. Aucun regroupement supplémentaire de HTTP/HTTPS, www, casse du chemin, slash final, paramètres ou fragments. Protocoles hors HTTP(S) et URL avec identifiants ne sont pas pris en charge. Ce contrôle ne teste pas la réponse HTTP d’un lien.

## Dates et précision

Formats : `YYYY-MM-DD` ; `YYYY-MM-DDTHH:mm:ss[.SSS]Z` ou offset explicite ; français `1 octobre 2026` / `1er octobre 2026` lorsque déclaré. Pas de devinette sur `04/05/2026`, fuseau implicite ou date libre. Calendrier, heures et offsets sont bornés. La timezone est un identifiant IANA, par exemple `Europe/Paris`.

Le texte sélectionné, l’attribut de valeur choisi et `datetime` lorsqu’il existe sont conservés. Une discordance du texte ne se cache pas derrière deux attributs égaux. Deux horodatages se comparent comme instants ; jour contre horodatage se compare au jour dans le fuseau du profil. Sans ce fuseau, le contrôle ne conclut pas. La précision disponible est conservée ; une comparaison au jour ne contrôle pas l’heure.

`datePublished` et `dateModified` sont des règles séparées. Des constats supplémentaires comparent l’ordre des dates balisées et leur position par rapport à la **date de référence explicite** du profil. Aucune date système ou de fichier n’est une preuve de révision. S6 démontre que deux valeurs erronées peuvent concorder ; seul le journal connu de la fixture révèle le défaut éditorial.

## Auteurs, FAQ et fil

Les auteurs sont Person/Organization inline ou références locales. Noms en ensemble, sauf ordre déclaré. Accents, casse et ponctuation conservés ; espaces normalisés et Unicode NFC. Des noms identiques dans plusieurs auteurs rendent l’appariement des URL ambigu. Nom et URL sont deux constats séparés, avec un troisième constat de résolution des références. Un nom différent signale une différence de valeur, pas une identité humaine différente. Les URL sont appariées par nom ; un nom muté peut donc produire aussi une différence de paires nom/URL, sans que l’URL elle-même ait changé.

Le fil extrait une séquence explicite libellé/destination. Positions JSON positives, uniques et contiguës dans ce profil. Pas d’inférence depuis le chemin URL. Un dernier élément non lié est accepté si les deux côtés n’ont pas de lien, ou peut correspondre à l’URL de page avec `lastUnlinked: page_url`. Un lien HTML présent sans href exploitable est non déterminé. Aucun accueil universellement obligatoire.

La FAQ associe les paires par `identifier` déclaré (attribut HTML choisi) ou texte normalisé de la question. Jamais par rang. Les doublons restent ambigus ; plusieurs réponses ne sont pas départagées. Les entités HTML, espaces et balises de présentation se normalisent, avec séparation des blocs et retours de ligne. Nombres, négations, ponctuation et casse ne sont pas effacés. Les liens sont comparés si `links: true`. Ce n’est pas une similarité sémantique.

## États, couverture, reproductibilité

| État JSON            | Interprétation                                                                         |
| -------------------- | -------------------------------------------------------------------------------------- |
| `concordant`         | Valeurs comparables et égales selon cette règle.                                       |
| `ecart`              | Différence observée ou règle explicite d’ordre/référence enfreinte.                    |
| `non_determine`      | Information absente, base/fuseau manquant, sélection ambiguë ou référence non résolue. |
| `non_pris_en_charge` | Construction hors du lecteur documenté.                                                |
| `non_applicable`     | Règle optionnelle dont le profil justifie explicitement l’absence des deux côtés.      |
| `invalide`           | Entrée, syntaxe, taille, date impossible ou configuration invalide.                    |

Aucun zéro fichier/règle/entité ne produit une réussite. Au moins une règle attendue est obligatoire. Une page n’est concordante sur le profil que si tous ses constats attendus aboutissent et qu’aucun script n’est partiellement exclu. Les catégories de pages du résumé peuvent se recouper ; 12 pages en écart ne signifient pas 12 constats.

Les rapports contiennent les empreintes SHA-256 des chaînes HTML décodées et du profil sérialisé avec clés triées. Ils identifient les entrées ; ils ne prouvent pas authenticité, vérité ou provenance réelle. Les constats sont déterministes ; `generatedAt` varie. Les versions moteur/profil/méthode sont distinctes.

## Limites et extension rendue

500 fichiers, 2 000 000 octets UTF-8 chacun, 30 000 000 octets cumulés, 40 niveaux JSON, 30 000 nœuds HTML ou JSON, profondeur HTML 200, 100 000 caractères par valeur JSON ou HTML extraite, 1 000 valeurs HTML par règle, 30 règles. Les extraits HTML sont plafonnés à 2 000 caractères et signalés comme tronqués ; les valeurs réellement comparées sont conservées séparément. Le worker rend la progression à chaque page et l’interface peut le terminer réellement. Le corpus de 200 pages est une mesure de recette, pas une garantie de temps pour 30 Mo de HTML arbitraire.

V2 éventuelle, non implémentée : collecteur avec URL et profil, condition explicite d’attente, timeout, horodatage, versions navigateur/collecteur, erreurs et DOM sauvegardé avec empreinte. Une attente fixe ne garantit pas que toutes les données ont chargé. La fixture JavaScript de la recette ne démontre qu’un cas local à condition `data-jsonld-ready=true`.

Les sources officielles et leur périmètre sont dans [SOURCES](SOURCES.md). Les validateurs existants restent utiles pour d’autres questions ; ce dépôt ajoute une revue de gabarit configurée.
