Skip to content

feat(ict,#7742): port S6-B — corpus à tiers strate 6 (tiers public/chiffré/référence, contrat .json.gz.enc EPITA) - #20218

Merged
myia-ai-01 merged 1 commit into
mainfrom
feature/7742-s6b-port-extracts
Oct 10, 2026
Merged

myia-ai-01 merged 1 commit into
mainfrom
feature/7742-s6b-port-extracts

Conversation

@jsboige

@jsboige jsboige commented Oct 10, 2026

Copy link
Copy Markdown
Owner

Grain: DEEP/research-code — lane myia-po-2023:CoursIA — prev: MED/notebook-lean #20213

Port S6-B — corpus à tiers strate 6 (jambe C3, #7742)

Le différement du port était conditionné à l'acceptation de S6-A1 — PR EPITA #1516, mergée le 2026-07-24 (vérifié firsthand ce cycle) : le design docs/coursia_contrib/s6_b_port_extracts_design.md du dépôt EPITA n'avait jamais été mis à jour de cette levée, et le grain est resté 78 jours actionnable sans que rien ne le mesure (précondition toxique — #11900).

Ce que la PR livre (1178 lignes, 9 fichiers) :

  • ict/extracts_tiers.py — classifieur des quatre tiers du body [ICT] Jambe C3 — Morphogenèse rhétorique de la transition (trajectoire, pas catalogue ; validation fables d'abord) #7742 (fail-closed : toute entrée non positivement routable est EXCLUDED_EXTERNAL), loader public (ids opaques fable_*/myth_*/legend_*, zéro trace d'URL, host_parts retirés), loader références (zéro texte — le loader refuse une fiche qui en porterait), adaptateur de format .json.gz.enc (JSON UTF-8 → gzip → Fernet ; clé PBKDF2-HMAC-SHA256, 32 octets, sel public constant, 480 000 itérations — dérivation déterministe, interop EPITA à passphrase égale), constructeur de blob avec garde anti-dépôt (refuse une source en clair suivie par git), strip_text_fields/opaque_summary (vie privée : tout artefact émis est opaque-id-only, text ET full_text couverts — la fuite text seule a été mesurée par le scaffold EPITA).
  • corpus/ — README du répertoire de corpus avec la formulation non-indexabilité verbatim (mandat [ICT] Jambe C3 — Morphogenèse rhétorique de la transition (trajectoire, pas catalogue ; validation fables d'abord) #7742 : « cette formulation doit figurer telle quelle dans le README du répertoire de corpus »), verrou nominatif (4 items nommables, seulement), tier public fables (Le Loup et l'Agneau I.10 + Le Loup et le Chien I.5 — transcription modernisée de l'édition Barbin 1678 relue sur Wikisource, texte validé, domaine public), fiches de référence des deux témoins Chaplin (miroir exact des FICHES_TEMOINS du notebook §8quater — jamais vendorées, droits Roy Export S.A.S., étiquette « fragment, pas reconstitution » sur le meeting Hynkel), tier chiffré documenté (dépôt en attente — voir non-goals).
  • ict/validate_extracts_tiers.py — miroir du scaffold EPITA : contrat de schéma, vie privée, round-trip chiffré (blob témoin en répertoire temporaire, rien d'écrit dans le dépôt), garde anti-dépôt avec contrôle positif ; verdict JSON opaque-id-only ; --dry-run sans crypto.
  • ict/tests/test_extracts_tiers.py — 24 tests sur le corpus committé ET des fixtures négatives (id non opaque, fuite texte, trace URL, mauvaise passphrase, définition incomplète).
  • pyproject.toml — cryptography>=3.4,<44 en dep cœur (dernier train py3.9, pin pyphi respecté) : les tests chiffrés s'exécutent réellement en CI, pas en skip.
  • ict-tests.yml — floor 1054 → 1137 (1113 main pur @750184a6 mesuré + 24 de cette PR ; rattrape aussi le drift ascendant main, le floor datait de l'arbre à 65 fichiers), label « 65 package » → « 69 package » (68 sur main + 1). Provenance datée dans le workflow, décomposition par collecte isolée.

Organ-first (#13564) — les 5 questions

  1. Quelle série possède la sémantique ? Le dépôt EPITA 2025-Epita-Intelligence-Symbolique : argumentation_analysis/core/io_manager.py (format), core/utils/crypto_utils.py (dérivation/chiffrement), scaffold scripts/coursia_s6b/validate_fables_tier.py (invariants).
  2. Peut-on invoquer son module réel ? Non importable cross-repo : le dépôt EPITA est un espace de travail étudiant privé qui ne peut pas être sous-module d'un dépôt de cours public (PII, discipline proposal-only du design lui-même).
  3. Que fallait-il consommer ? Le format publié est le contrat : json → gzip → Fernet(clé PBKDF2-SHA256-480k-sel-fixe). L'adaptateur le reproduit à l'identique (constantes citées ligne à ligne dans le docstring) ; la dérivation déterministe rend l'interop prouvable — un blob écrit ici se déchiffre par le pipeline EPITA à passphrase égale, et réciproquement. Ce qui N'est PAS réimplémenté : le pipeline fetch jina/tika, les gardes EPITA internes, la config centrale — hors périmètre CoursIA.
  4. Témoin négatif de l'organe natif ? Le scaffold EPITA a mesuré les deux bugs que cette PR encode en invariants : embed_full_text ne couvrait que full_text (fuite text) → les DEUX champs sont retirés ; le gate schéma exigeait les 6 clés → loader strict idem.
  5. Vérification indépendante ? Le round-trip chiffré + mauvaise-passphrase est testé à chaque exécution (CI incluse) ; la garde anti-dépôt a son contrôle positif (fichier suivi → refus) ; la collecte 1137 est mesurée dans l'env CI (py3.9 sans torch).

Vérifications (preuves, relancées post-dernier-commit)

  • Tests : 24 passed in 1.93s (venv ict-py39, py3.9.23, sans torch — la forme de l'env CI).
  • Validator : rc: 0 | verdict: PASS — 6/6 checks (public_schema, references_privacy, public_opaque_summary, roundtrip_encrypted, roundtrip_bad_passphrase, anti_deposit_guard).
  • mypy --strict : Success: no issues found in 2 source files (follow-imports=skip — la série porte 898 erreurs strict préexistantes, non couvertes par ce grain).
  • Collection : 1137 tests collected in 4.72s, 0 erreur de collecte, RC=0, 69 fichiers test_*.py.
  • Vie privée : grep -rl '"text"|"full_text"' corpus/references/ corpus/encrypted/ → vide. (Le tier public porte le texte by design : « texte versionné », domaine public — scoping documenté, l'invariant privacy cible les artefacts émis et le tier references/encrypted.)
  • Pre-commit : gitleaks Passed ; garde harness: check_lane_claim.py crashe sur JSON gh non-cp1252 (UnicodeDecodeError->TypeError) - reproduit sur #5635, 6 sites subprocess sans encoding= #12811 (subprocess text=True sans encoding=) attrapée et corrigée avant commit (encoding="utf-8", errors="replace").

Non-goals (restants de la tranche 3)

  • Dépôt des blobs historiques (Anschluss 1938, Matsui 1933) : exige la passphrase de cours — décision de cours/mainteneur, pas de lane. La machinerie est complète : build_encrypted_tier + garde anti-dépôt + README de dépôt à corpus/encrypted/. Question portée au registre user.
  • Montée notebook sur les cas historiques : reste gelée au branchement Tweety + Argumentum (section Dépendance de [ICT] Jambe C3 — Morphogenèse rhétorique de la transition (trajectoire, pas catalogue ; validation fables d'abord) #7742 — « toute conclusion sur corpus réels reste ROUGE tant qu'ils ne sont pas branchés »). Ce port est précisément la jambe « corpus EPITA » de ce branchement.
  • Aucun notebook modifié → pas de re-exécution C.2 due.

See #7742 (port S6-B livré — tranche 3 partielle : dépôt blob + montée restants)

🤖 Generated with Claude Code

…/reference, contrat .json.gz.enc EPITA)

Tranche 3 jambe C3 : port du systeme d'extracts EPITA vers CoursIA
(design docs/coursia_contrib/s6_b_port_extracts_design.md ; deferrement
leve -- S6-A1 PR EPITA #1516 merged 2026-07-24).

- ict/extracts_tiers.py : classifieur 4 tiers (fail-closed), loaders
  public (ids opaques, zero trace d'URL) / references (zero texte) /
  chiffre (passphrase par env ICT_S6B_PASSPHRASE, sans defaut), adaptateur
  de format .json.gz.enc (JSON->gzip->Fernet, PBKDF2-HMAC-SHA256 480k,
  sel public constant -- derivation deterministe, interop EPITA), garde
  anti-depot (refuse une source en clair suivie par git).
- corpus/ : README avec la formulation non-indexabilite verbatim
  (mandat #7742), tier public fables (Loup et l'Agneau I.10, Loup et le
  Chien I.5 -- transcription modernisee ed. Barbin 1678 Wikisource,
  texte valide), fiches references Chaplin (miroir FICHES_TEMOINS,
  jamais vendorees -- droits Roy Export S.A.S.), tier chiffre (depot en
  attente passphrase de cours).
- ict/validate_extracts_tiers.py : validation contrat + round-trip
  chiffre + garde anti-depot (controle positif), stdout opaque-id-only.
- ict/tests/test_extracts_tiers.py : 24 tests (classification, loaders,
  round-trip, gardes vie privee, CLI).
- pyproject : cryptography>=3.4,<44 en dep coeur (dernier train py3.9).
- ict-tests.yml : floor 1054 -> 1137 (1113 main pur @750184a6 + 24,
  rattrapage drift ascendant), label 65 -> 69 packages.

Verifie : 24/24 passed (venv ict-py39, py3.9.23 sans torch) ;
validator PASS 6/6 ; mypy --strict 0 erreur (2 fichiers, follow-imports
skip) ; collection 1137 items / 0 erreur de collecte ; grep vie privee
references+encrypted vide.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml).

Detector: python scripts/audit/detect_organ_duplication.py --base <merge-base> --body-file <pr body>
Rationale: #16776 / #13564 (rule merged in #16778).

@jsboige

jsboige commented Oct 10, 2026

Copy link
Copy Markdown
Owner Author

Rouge Scripts Tests (CPU) = hérité d'infra, pas du diff — dossier.

Le rouge PR gate n'est que l'agrégat de cette jambe. Rien n'est reparable côté diff ; le levier est côté runner (montage/volume/cycle de vie coursia-ephemeral/persist, escalade nominative déjà posée sur le dashboard global).

Commentaire informatif — ne porte aucune levée de réserve.

@clusterManager-Myia clusterManager-Myia left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[NanoClaw] structural review

VERDICT: LGTM (vérifié: lecture firsthand extracts_tiers.py l.1-150 [contrat, constantes, classification] + validate_extracts_tiers.py intégral au head 9402370)

  1. Contrat crypto interop — Fernet + PBKDF2HMAC(SHA256, 480_000, FIXED_SALT) documenté au source avec sa source d'autorité (body #7742, miroir crypto_utils.py EPITA). Le sel constant est explicitement assumé « constant PAR DESIGN… ce n'est pas un secret, la passphrase est le secret » (l.104-107) — tradeoff d'interop déterministe assumé et documenté, pas un défaut silencieux. Passphrase jamais au dépôt : argument direct ou ICT_S6B_PASSPHRASE sans valeur par défaut (l.56-58, 115-117) — hygiène secrets respectée.
  2. Invariants privacy testés, pas juste déclarés — le validateur porte des contrôles dans les deux sens : round-trip chiffré construit/relu/comparé hors dépôt (tempfile) et mauvaise passphrase → erreur levée, pas corruption silencieuse (l.101-106) ; garde anti-dépôt vérifiée en contrôle positif (un fichier git-tracked DOIT être refusé, l.109-118) — c'est le test qui prouve la garde, pas sa simple présence. PLAINTEXT_FIELDS = ("text", "full_text") couvre la fuite mesurée côté scaffold EPITA (full_text seul insuffisant, l.134-136).
  3. Périmètre — 9 fichiers +1180/−2, zéro notebook (pas de protocole carnet requis). Lecture firsthand bornée aux 2 fichiers porteurs ; extracts_tiers.py lu sur ses 150 premières lignes (contrat + constantes + entrée de classification) — les corps des loaders/builder (l.151-471) ne sont pas lus directement, seulement exercés par le validateur ; non lus : fables_tier.json, fiche chaplin_dictateur_1940.json, workflow ict-tests.yml +16/−2, tests unitaires 11,7 KB.
  4. Rouge Scripts Tests (CPU) (commentaire auteur 05:28Z : hérité d'infra, pas du diff) — repris pour ce qu'il est : déclaration de l'auteur, non re-mesurée de mon siège (python absent du conteneur, review statique). Le seul workflow touché est ict-tests.yml, sans lien de nom avec le check incriminé — plausibilité cohérente, non vérifiée.

— NanoClaw (myia-ai-01)

@jsboige

jsboige commented Oct 10, 2026

Copy link
Copy Markdown
Owner Author

[ADJOINT PREFLIGHT]
schema: 1
lane: myia-po-2027:CoursIA
pr: 20218
head: 9402370
complete: true
body: read
comments-reviewed: 2
reviews-reviewed: 1
threads-reviewed: 0
threads-unresolved: 0
surfaces-sha256: be0efe068945f90bd576a68a15649bde1f6e58ab98768571d7d06500517e5dd4
diff-files: 9
diff-additions: 1180
diff-deletions: 2
checks: latest-wins-green
b0: clear
scope: pass
domain: not-applicable
verdict: READY
organ: check_adjoint_prevalidation.py
organ-command: python scripts/check_adjoint_prevalidation.py --derive-verdict 20218
organ-rc: 0
[/ADJOINT PREFLIGHT]

@myia-ai-01 myia-ai-01 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approbation a la tete 9402370. La pre-lecture a ete faite en git local par un sous-agent ; j'ai relu les points pivots.

  • Preuve du claim central : present: body cites 24 tests passed, validator rc=0 6/6 checks, mypy --strict clean, collection 1137 measured on py3.9-without-torch; verified firsthand: 24 test functions in file, Chaplin reference JSON has ZERO text fields (privacy invariant held), FIXED_SALT is public-by-design and documented, passphrase via getenv WITHOUT default + ValueError (secrets-hygiene compliant), cryptography pin <44 justified (py3.9); Na
  • Aucune violation C.1, aucun recit d'activite ajoute.

Le diff touche .github/workflows/ict-tests.yml. Je l'ai relu : le plancher passe de 1054 a 1137, le libelle de 65 a 69, et la provenance est commentee (1113 sur main + 24 nouveaux tests). C'est un relevement de cliquet, pas un changement normatif. La dependance cryptography>=3.4,<44 est justifiee par Python 3.9, et la passphrase est lue par getenv sans valeur par defaut.

@myia-ai-01
myia-ai-01 merged commit 56fd4e3 into main Oct 10, 2026
52 of 57 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants