ADR-0010 — Corpus public

Status: Proposed (2026-04-29, après design review interne).

Note de numérotation. L'intitulé de travail initial était 0002-corpus-public.md. Le slot 0002 étant déjà occupé par byte-deterministic-cli (conséquence d'une revue de design antérieure), l'ADR prend le prochain numéro libre (0010) — la numérotation du dépôt est monotone, jamais réécrite. L'identité conceptuelle ne change pas : c'est l'acte qui nomme comment le corpus public existe.

Context

morpion-play.pages.dev est une PWA produisant des traces NDJSON v1 (docs/schemas/game-record-v1.md) lorsqu'un joueur termine une partie. Ces traces, ensemble, forment un corpus humain de Morpion Solitaire 5T — l'objet scientifique à publier vers partnerships, Mallat, et la communauté chercheuse cognition / ML qui pourrait l'utiliser pour étudier la planification combinatoire chez l'enfant.

Le projet morpion adopte une doctrine simple sur l'orientation du flux entre privé et commun : privé \(\to\) commun, public \(\to\) commun, jamais l'inverse. Le test produit qui en découle est explicite : si quelqu'un peut désigner une trace privatisable, une stratégie verrouillée, un score lockable, on a échoué.

Sans cet ADR, le corpus existe juridiquement (les traces sont collectées sous opt-in après le pass RGPD default-off) mais pas narrativement : aucun chemin lisible « où est le commun, sous quelle licence, comment le citer ». La conséquence est qu'un concurrent peut en cinq jours déployer un dataset CC-BY + DOI Zenodo et nous dépasser silencieusement. La défense la moins coûteuse est non pas de produire le corpus plus tôt, mais de le nommer publiquement avant qu'il ne contienne quoi que ce soit — geste-licence + endpoint /dataset + ADR.

La revue interne a convergé sur trois arbitrages cardinaux que cet ADR formalise :

Decision

1. Cadence de publication

Le cron du workflow n'est pas activé dans cette ADR. Le brouillon livré est workflow_dispatch only — la première release sera déclenchée manuellement par l'opérateur après que morpion-cer-pre-enregistrement aura outillé l'anonymisation côté serveur (SHA-256 du player_id avec sel non public, drop de started_at_iso, drop de screen_w/h/dpr).

2. Promotion Zenodo

3. Licences

ArtefactLicenceJustification
Traces NDJSON (traces/*.jsonl)CC0-1.0Données factuelles sans expression créative. Test produit : sous CC0, personne ne peut s'approprier plus qu'autrui. Zéro friction d'adoption en classe (partnerships) — le prof ne lit aucune licence avant de rejouer une partie.
Prose accompagnante (README.md du release, docs/corpus/*, notes méthodologiques)CC-BY-SA 4.0Toute redérivation reste partageable. La paire CC0+CC-BY-SA respecte la doctrine du commun adoptée pour ce projet.
Code de la PWA, du workflow, des outils (dist-play/, tools/, .github/workflows/corpus-release.yml, dist-play/functions/dataset.js)MITAdoption maximale aval — partnerships, un labo, un fork pédagogique peuvent réutiliser le code sans friction de copyleft. La traction inverse est explicitement souhaitée : si un fork apparaît, c'est un succès.

ODbL est rejetée (sur-spécifiée, friction d'adoption en classe). AGPL sur le code est rejetée (la PWA n'a pas de boucle SaaS à protéger — la valeur n'est pas dans le code, elle est dans le commun de traces).

4. Anonymisation (préalable à la première release)

L'implémentation effective est portée par la molécule morpion-cer-pre-enregistrement (warm). Cet ADR fixe le contrat ; la molécule fixe l'exécution :

Avant cette implémentation, aucune trace ne sort du repo en release publique. Le workflow corpus-release.yml livré ce soir échoue intentionnellement si l'anonymisation n'est pas en place (vérification de présence d'un salt-version.txt dans le secret du runner).

5. Citation

Pré-Zenodo (par défaut, jusqu'au premier dépôt qualifié) :

Sérié, E. & contributeurs anonymes (2026). Morpion Solitaire 5T human trace corpus, release 2026-MM. https://github.com/eserie/morpion/releases/tag/corpus-2026-MM

Post-Zenodo (à partir du premier DOI émis) :

Sérié, E. & contributeurs anonymes (2026). Morpion Solitaire 5T human trace corpus, release 2026-MM. Zenodo. https://doi.org/10.5281/zenodo.XXXXXXX

La citation est régénérée automatiquement dans le README.md du release par corpus-release.yml. Le XXXXXXX est remplacé par le DOI réel après le premier dépôt Zenodo (variable de workflow ZENODO_CONCEPT_DOI).

6. Endpoint /dataset

Une Cloudflare Pages Function (dist-play/functions/dataset.js) répond :

L'endpoint se contente de proxifier l'API GitHub Releases publique ; il n'a ni base de données, ni cache, ni état. Sa seule valeur est la lisibilité : un point d'entrée stable et nommé. Latence acceptable \((\leq\) 500 ms p95) — l'usage est documentaire, pas chaud.

Architecture

                    ┌─────────────────────────────────┐
                    │  PWA dist-play/                 │
                    │  share-traces opt-in (gate ✔)   │
                    └────────────────┬────────────────┘
                                     │ POST /save (opt-in only)
                                     ▼
                    ┌─────────────────────────────────┐
                    │  Cloudflare Worker save.js      │
                    │  + R2 storage (privé)            │
                    │  + sync mensuel → repo gallery/ │
                    └────────────────┬────────────────┘
                                     │ git push corpus/
                                     ▼
              ┌──────────────────────────────────────────────┐
              │  GitHub Actions corpus-release.yml           │
              │  - filter share-traces === '1'               │
              │  - count distinct hashed player_id           │
              │  - tar.zst + manifest.csv + README           │
              │  - create release corpus-YYYY-MM             │
              └────────────┬─────────────────────────────────┘
                           │
            ┌──────────────┼──────────────────────────────┐
            ▼                                              ▼
   ┌─────────────────────┐                  ┌──────────────────────────┐
   │ GitHub Release      │  ←─── /dataset ──┤ Cloudflare Function      │
   │ (mensuel, CC0+CC-BY │   pointe vers    │ dist-play/functions/     │
   │ -SA + MIT)          │     le dernier   │ dataset.js               │
   └──────────┬──────────┘                  └──────────────────────────┘
              │
              │ semestriel, ≥ 200 joueurs distincts
              ▼
   ┌─────────────────────┐
   │ Zenodo concept DOI  │
   │ (citable, archivé)  │
   └─────────────────────┘

Consequences

Invariant

Toute trace publiée dans un release corpus-YYYY-MM est sous CC0-1.0, son player_id est haché avec un sel non public, ne contient aucun horodatage plus fin que le jour, et n'a jamais transité par un chemin de POST qui ignore la gate shouldPostTrace(). Aucune autre voie ne porte la mention « corpus public ».


Derivation — Issue d'une revue interne du projet morpion ayant produit cinq décisions verrouillées par cet ADR : cadence mensuelle, promotion Zenodo semestrielle, seuil 200 joueurs distincts, paire de licences CC0 + CC-BY-SA, et footer public obligatoire. La revue a aussi identifié le risque d'un concurrent préemptant un dataset équivalent en cinq jours — d'où la décision de nommer publiquement le commun avant qu'il ne contienne quoi que ce soit (geste-licence + endpoint /dataset + ADR). L'arbitrage de licence CC0 sur les traces (vs CC-BY-4.0) repose sur la doctrine du commun adoptée pour ce projet : test produit explicite (rien que sous CC0 ne peut redevenir privatisé) et flèche orientée privé \(\to\) commun, jamais l'inverse.