feat(session-0): cadre juridique, porte de validation et dataset vérifié
Session 0 du plan — aucun code applicatif. Cadre - 5 ADR : licences, Phaser 4 hors budget initial, frontière Svelte/Phaser, filières A/B des assets, kréyòl langue principale (norme gwadloupéyenne). - LICENSE (AGPL-3.0-or-later, texte canonique gnu.org), LICENSE-CONTENT (CC BY-SA 4.0). Le jeu est une œuvre collective : aucun compositing sur un média SA ou NC, contrainte vérifiée par script. - Audit des 7 sources avec endpoints réellement appelés. TRAMIL vérifié en direct : « all rights reserved » et page de mentions légales en 404. iNaturalist mesuré à 93 % de CC BY-NC sur un échantillon de 305 photos. Outillage (tools/data/, hors bundle, zéro appel réseau au runtime) - verify:taxa, ingest:commons, build:dataset, validate:data, test:porte, build:credits, check:audio. - validate.mjs est la porte exigée par le brief §2 : le build échoue si une épreuve s'appuie sur un média non validé, non photographique ou de filière B, sur une attribution sans auteur, sur un compositing SA/NC, ou sur une entrée publiée sans relecture kréyòl. test:porte prouve les 9 refus + 1 témoin. Données - 13 entrées (8 fruits + 5 plantes du module Pa touché) vérifiées contre Wikidata ET GBIF : 13/13 ACCEPTED, 0 divergence avec la graine. - 130 candidats Commons sous licence libre, 39 pré-inscrits, 0 validé — aucun agent ne peut valider un média. Écarts signalés, non corrigés en silence - Le champ danger de la graine a deux formes (note vs resume) : normalisé. - 3 identifiants divergent du nom kréyòl au-delà des accents : non tranchés. - comestible:false sur akée et pomme-cajou : sous-estimation prudente assumée. - Le mancenillier n'a aucune photographie moderne dans ses 3 premiers candidats — l'entrée la plus critique est la moins bien servie. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* build-credits.mjs — génère CREDITS.md depuis les données.
|
||||
*
|
||||
* Le brief §3 exige un CREDITS.md « généré depuis les données ». Un fichier de crédits
|
||||
* tenu à la main se désynchronise le jour où l'on remplace une photo, et une attribution
|
||||
* fausse est une violation de licence. Ce fichier n'est donc jamais édité : il se rejoue.
|
||||
*
|
||||
* Les médias sous clause non commerciale sont isolés dans leur propre section, comme
|
||||
* l'ADR 0001 l'impose.
|
||||
*/
|
||||
|
||||
import { readFile, readdir, writeFile } from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..');
|
||||
const DATA = join(ROOT, 'src', 'lib', 'data', 'fruits');
|
||||
|
||||
const LIBELLE = {
|
||||
'CC0-1.0': 'CC0 1.0 (domaine public volontaire)',
|
||||
PD: 'domaine public',
|
||||
'CC-BY-2.0': 'CC BY 2.0',
|
||||
'CC-BY-3.0': 'CC BY 3.0',
|
||||
'CC-BY-4.0': 'CC BY 4.0',
|
||||
'CC-BY-SA-2.0': 'CC BY-SA 2.0',
|
||||
'CC-BY-SA-2.5': 'CC BY-SA 2.5',
|
||||
'CC-BY-SA-3.0': 'CC BY-SA 3.0',
|
||||
'CC-BY-SA-4.0': 'CC BY-SA 4.0',
|
||||
'CC-BY-NC-4.0': 'CC BY-NC 4.0',
|
||||
'CC-BY-NC-SA-4.0': 'CC BY-NC-SA 4.0',
|
||||
'OKI-CC-BY-SA-4.0': 'ORGANISATION KA INTERNATIONALE, CC BY-SA 4.0'
|
||||
};
|
||||
|
||||
const echapper = (s) => String(s ?? '—').replace(/\|/g, '\\|').replace(/\n/g, ' ');
|
||||
|
||||
async function main() {
|
||||
const fichiers = (await readdir(DATA)).filter((f) => f.endsWith('.json')).sort();
|
||||
const entrees = [];
|
||||
for (const f of fichiers) entrees.push(JSON.parse(await readFile(join(DATA, f), 'utf8')));
|
||||
|
||||
const tous = entrees.flatMap((e) => e.medias.map((m) => ({ ...m, entree: e })));
|
||||
const nc = tous.filter((m) => m.usage_non_commercial);
|
||||
const sa = tous.filter((m) => m.partage_identique);
|
||||
|
||||
const l = [
|
||||
'# Crédits — KI FWI SA YÉ ?',
|
||||
'',
|
||||
'> **Fichier généré.** Ne pas éditer à la main : `npm run build:credits`.',
|
||||
`> Dernière génération : ${new Date().toISOString().slice(0, 10)}.`,
|
||||
'',
|
||||
'## Ce que couvre ce fichier',
|
||||
'',
|
||||
'Chaque média embarqué dans le jeu ou dans l’encyclopédie est listé ci-dessous avec son',
|
||||
'auteur, sa licence et le lien vers sa page de description d’origine. Rien n’est',
|
||||
'« libre de droit » : chaque ligne est une obligation contractuelle.',
|
||||
'',
|
||||
'Le code du projet est sous **AGPL-3.0-or-later** (`LICENSE`). Les textes rédigés par',
|
||||
'ORGANISATION KA INTERNATIONALE sont sous **CC BY-SA 4.0** (`LICENSE-CONTENT`). Les médias',
|
||||
'tiers restent sous leur licence propre : le jeu est une **œuvre collective**, pas une',
|
||||
'œuvre dérivée — voir `docs/adr/0001-licences.md`.',
|
||||
'',
|
||||
`## Chiffres`,
|
||||
'',
|
||||
`- ${entrees.length} entrées · ${tous.length} médias`,
|
||||
`- ${tous.filter((m) => m.valide).length} médias validés par un humain`,
|
||||
`- ${sa.length} médias en partage à l’identique (CC BY-SA) — **aucun compositing autorisé**`,
|
||||
`- ${nc.length} médias sous clause non commerciale (CC BY-NC)`,
|
||||
''
|
||||
];
|
||||
|
||||
if (nc.length) {
|
||||
l.push(
|
||||
'## ⚠ Médias sous clause non commerciale',
|
||||
'',
|
||||
'La présence de ces médias engage le projet à ne **jamais** être distribué à titre',
|
||||
'commercial — ni vente, ni version payante, ni licence à un éditeur. Décision prise et',
|
||||
'documentée dans `docs/adr/0001-licences.md`. Retirer ces médias est la seule façon de',
|
||||
'revenir en arrière.',
|
||||
'',
|
||||
'| entrée | média | auteur | licence | source |',
|
||||
'|---|---|---|---|---|'
|
||||
);
|
||||
for (const m of nc)
|
||||
l.push(
|
||||
`| \`${m.entree.id}\` | ${echapper(m.id)} | ${echapper(m.auteur)} | ${LIBELLE[m.licence] ?? m.licence} | [page](${m.url_description}) |`
|
||||
);
|
||||
l.push('');
|
||||
}
|
||||
|
||||
l.push('## Médias par entrée', '');
|
||||
for (const e of entrees) {
|
||||
l.push(`### ${e.noms.gcf} — *${e.taxon.scientifique}* (\`${e.id}\`)`, '');
|
||||
l.push(
|
||||
`Taxonomie vérifiée le ${e.taxon.verifie_le} contre [Wikidata ${e.taxon.wikidata}](https://www.wikidata.org/wiki/${e.taxon.wikidata})`,
|
||||
`et [GBIF ${e.taxon.gbif}](https://www.gbif.org/species/${e.taxon.gbif}).`,
|
||||
''
|
||||
);
|
||||
if (!e.medias.length) {
|
||||
l.push('_Aucun média._', '');
|
||||
continue;
|
||||
}
|
||||
l.push('| média | vue | auteur | licence | validé | page d’origine |');
|
||||
l.push('|---|---|---|---|---|---|');
|
||||
for (const m of e.medias)
|
||||
l.push(
|
||||
`| ${echapper(m.id)} | ${m.vue ?? '—'} | ${echapper(m.auteur)} | ${LIBELLE[m.licence] ?? m.licence} | ${m.valide ? `oui (${echapper(m.valide_par)})` : 'non'} | [voir](${m.url_description}) |`
|
||||
);
|
||||
l.push('');
|
||||
}
|
||||
|
||||
l.push(
|
||||
'## Sources factuelles',
|
||||
'',
|
||||
'- **Wikidata** — identifiants et taxonomie (CC0). https://www.wikidata.org',
|
||||
'- **GBIF** — noms scientifiques acceptés et clés d’usage. https://www.gbif.org',
|
||||
'- **Wikimedia Commons** — photographies. https://commons.wikimedia.org',
|
||||
'',
|
||||
'Voir `SOURCES.md` pour le détail des régimes de licence et `docs/audit-sources.md`',
|
||||
'pour l’audit complet, TRAMIL et CIRAD compris.',
|
||||
''
|
||||
);
|
||||
|
||||
await writeFile(join(ROOT, 'CREDITS.md'), l.join('\n'));
|
||||
console.log(`✔ CREDITS.md — ${entrees.length} entrées, ${tous.length} médias, ${nc.length} sous clause NC.`);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error('✘', e.stack ?? e.message);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user