# Capordia — exercice de bascule et reprise, version 1.0.0

Cet exercice CPU compare un traitement continu à un traitement interrompu,
copié dans un autre dossier puis repris dans un nouveau processus. Il permet
de constater quels lots sont terminés, en cours et à reprendre. Il ne mesure
aucun GPU et ne démontre pas une migration sans interruption.

## Préparer l'exercice

Utiliser Python 3.10 ou plus récent avec les modules standards `sqlite3` et
`hashlib`, sur un disque local inscriptible. Aucun paquet, accès réseau,
compte, secret ou administrateur n'est nécessaire. Extraire tous les fichiers
du dossier, puis ouvrir un terminal dans ce dossier. Si le système utilise
`python3` ou `py -3`, remplacer `python` dans les commandes. Le script et le
jeu d'entrées restent séparés des dossiers de travail qu'il crée.

Ne pas exécuter cet exercice dans un dossier de production ou sur un partage
réseau. Choisir un nom de dossier de travail qui n'existe pas : le programme
refuse de réutiliser ou d'écraser un dossier existant.

```text
python exercice.py demonstration --dossier essai-capordia
```

Cette commande crée `essai-capordia/rapport-verification.json` et les dossiers
de chaque scénario. Elle se termine par le code 0 seulement si tous les
contrôles sont réussis. Relancer avec un nouveau nom pour un nouvel essai.
La commande n'efface aucun essai précédent.

## Notre exemple : huit lots de coupons fictifs

Les huit CSV contiennent chacun six mesures dimensionnelles originales et
fictives. Chaque ligne contient `coupon`, `cible_um` et `mesure_um`. Les unités
sont des micromètres entiers. Aucun chiffre ne vient d'une machine de
production ou d'un client. Pour chaque lot, le traitement calcule le nombre
de coupons, la somme et le maximum des écarts absolus, et le nombre d'écarts
inférieurs ou égaux à 15 µm. Ce seuil est une règle d'exercice, pas une
tolérance industrielle recommandée.

Le résultat contient le numéro de lot, la version de la recette et l'empreinte
SHA-256 de l'entrée. L'usage d'entiers permet ici une comparaison exacte.
Une charge GPU avec des calculs flottants nécessiterait sa propre règle de
comparaison et des tolérances justifiées.

## Lire les états

| État dans le registre | Sens dans cet exercice | Traitement à la reprise |
|---|---|---|
| `termine` | Résultat accepté et état enregistré dans une même transaction | Vérifier ; ne pas recalculer |
| `en_cours` | Tentative déclarée, aucun résultat accepté | Examiner l'ancienne autorité puis autoriser la reprise |
| `a_reprendre` | Lot non commencé ou tentative explicitement remise en file | Exécuter une tentative |

Après la coupure injectée sur LOT-004 : LOT-001 à LOT-003 sont terminés,
LOT-004 est en cours, LOT-005 à LOT-008 sont à reprendre. Aucun résultat de
LOT-004 n'est accepté. Après `preparer-reprise`, il reste trois terminés et
cinq à reprendre. À la fin, les huit résultats doivent être égaux à ceux
de la référence. Seul LOT-004 a deux tentatives.

## Refaire la bascule pas à pas

Les chemins suivants sont relatifs au dossier qui contient `exercice.py`.
La coupure est une exception injectée après le calcul et avant acceptation ;
le code 75 à cette étape est attendu. Le processus s'arrête et libère son
verrou. Ce n'est ni un arrêt brutal de l'ordinateur ni une panne électrique.

```text
python exercice.py initialiser --dossier manuel/reference --proprietaire reference
python exercice.py executer --dossier manuel/reference
python exercice.py initialiser --dossier manuel/source --proprietaire atelier-source
python exercice.py executer --dossier manuel/source --interrompre-sur LOT-004
python exercice.py etat --dossier manuel/source
python exercice.py basculer --source manuel/source --destination manuel/cible --proprietaire atelier-cible
python exercice.py preparer-reprise --dossier manuel/cible
python exercice.py executer --dossier manuel/cible
python exercice.py comparer --reference manuel/reference --candidat manuel/cible
```

`basculer` prend le verrou local, contrôle le jeu, clôture la source, ferme
les connexions SQLite et copie le dossier. La copie reste clôturée jusqu'à
la fin de sa vérification ; seule la cible est ensuite autorisée à produire.
Une commande d'exécution lancée sur la source clôturée retourne le code 2
avec `SOURCE_CLOTUREE`.

La commande de comparaison exige huit résultats acceptés de chaque côté,
vérifie leurs entrées et leurs sorties, puis compare une sérialisation JSON
canonique des résultats. Les exports texte peuvent être reconstruits depuis
SQLite : ils ne sont pas l'autorité qui décide si un résultat est accepté.

## Les fichiers produits

| Fichier | Rôle |
|---|---|
| `registre.sqlite` | État de référence, tentatives, propriétaire, clôture et résultats acceptés |
| `manifeste.json` et `donnees/` | Jeu d'entrées figé avec les empreintes attendues |
| `etat.json` | État lisible et décompte des trois statuts |
| `registre-lots.csv` | Export : id, etat, tentatives, entree_sha256, sortie_sha256 |
| `resultats.json` | Export des résultats acceptés, dans l'ordre des identifiants |
| `rapport-verification.json` | Dans le dossier global de démonstration : contrôles, environnement et sorties des commandes |
| `worker.lock` | Verrou temporaire de notre programme, retiré lors de sa sortie normale ou de la coupure injectée |

Le fichier `registre-lots-vierge.csv` fourni dans la ressource est un modèle
plus large pour votre équipe. Il ajoute responsable, machine autorisée,
critère d'acceptation et raison de reprise. Il ne remplace pas la base de
l'exercice et n'est pas importé par le programme.

## Les erreurs vérifiées

La démonstration teste aussi la relance d'un travail terminé, le rejet d'une
seconde acceptation du même lot, une entrée modifiée, un identifiant dupliqué
dans le manifeste, une sortie altérée, un verrou déjà présent et une exception
à l'intérieur de la transaction d'acceptation. Ce dernier cas laisse trois
sorties acceptées, pas quatre, puis retrouve les huit résultats après reprise.
Le retour est testé en copiant l'état récent de la cible dans un troisième
dossier, en clôturant la cible. On ne réactive pas l'ancien instantané.

Les codes de sortie sont : 0 réussite ; 2 refus métier attendu ; 75 coupure
injectée attendue ; 1 erreur de lecture ou de base. Le scénario global attend
explicitement les refus qu'il provoque. Une autre erreur fait échouer l'essai.

## Limites à garder avec les résultats

Un seul worker participe à cet exercice. Le verrou et le champ de clôture
fonctionnent dans les dossiers locaux manipulés par ce programme. Ils
n'empêchent pas une personne de recopier la base ou un autre programme
d'ignorer le verrou. Ce n'est pas du fencing distribué. Avant une véritable
bascule, l'ancienne machine doit perdre son droit de publier par un mécanisme
indépendant et testé. Si son arrêt n'est pas confirmé, on ne démarre pas une
seconde autorité.

Une transaction regroupe ici résultat et statut dans **la même base locale**.
Elle ne couvre pas un envoi vers un système externe, une notification ou un
fichier déjà consommé ailleurs. L'absence de double acceptation vérifiée se
limite à cette base et à cet exercice. SHA-256 détecte ici un écart entre
octets et empreinte attendue ; un manifeste modifiable avec les données
n'authentifie pas leur origine.

Si un processus est tué brutalement, le verrou peut rester présent. Vérifier
son arrêt réel avant toute intervention manuelle ; l'exercice ne supprime
jamais automatiquement un verrou jugé ancien. Une interruption de copie
laisse la source clôturée : conserver les dossiers et examiner l'incident,
sans remettre deux copies en activité. La perte d'alimentation, les systèmes
de fichiers réseau, la concurrence entre machines, les pilotes GPU, les
checkpoints de frameworks et les performances ne sont pas qualifiés ici.

## Résultat de notre contrôle

Le 24 septembre 2026, sous Windows AMD64, Python 3.14.6 et SQLite 3.50.4 :
16 contrôles réussis, 37 commandes exécutées, 8 résultats acceptés identiques
après bascule. La preuve consultable est `preuve-execution-cpu.json`.
L'empreinte des résultats canoniques est
`bde1bc833092cc8ca481cda5f44cfef36f14f0a94dff3f856624e169f5456660`.
Ces chiffres décrivent ce jeu de 48 lignes et ne sont pas un benchmark GPU.

Voir [PROCEDURE.md](PROCEDURE.md) pour les responsabilités et le retour,
[SOURCES.md](SOURCES.md) pour les sources primaires, et [LICENSE.txt](LICENSE.txt)
pour les conditions de réutilisation.
