# Capordia — bài tập chuyển đổi và tiếp tục, phiên bản 1.0.0

Bài tập CPU này so sánh xử lý liên tục với xử lý bị gián đoạn,
được sao chép sang một thư mục khác rồi tiếp tục trong một tiến trình mới. Nó cho phép
nhận biết những lô nào đã hoàn thành, đang xử lý và cần tiếp tục. Nó không đo
bất kỳ GPU nào và không chứng minh được việc di chuyển không gián đoạn.

## Chuẩn bị bài tập

Dùng Python 3.10 hoặc mới hơn với các mô-đun chuẩn `sqlite3` và
`hashlib`, trên một đĩa cục bộ ghi được. Không cần gói, quyền truy cập mạng,
tài khoản, bí mật hay quyền quản trị viên nào. Giải nén toàn bộ tệp
trong thư mục, rồi mở một terminal trong thư mục đó. Nếu hệ thống dùng
`python3` hoặc `py -3`, thay `python` trong các lệnh. Script và
tập đầu vào vẫn tách biệt với các thư mục làm việc mà nó tạo ra.

Không chạy bài tập này trong một thư mục sản xuất hay trên một chia sẻ
mạng. Chọn một tên thư mục làm việc chưa tồn tại: chương trình
từ chối dùng lại hoặc ghi đè lên một thư mục đã có.

```text
python exercice.py demonstration --dossier essai-capordia
```

Lệnh này tạo `essai-capordia/rapport-verification.json` và các thư mục
của từng kịch bản. Nó kết thúc với mã 0 chỉ khi mọi
kiểm tra đều thành công. Chạy lại với một tên mới cho một lần thử mới.
Lệnh không xóa bất kỳ lần thử nào trước đó.

## Ví dụ của chúng ta: tám lô phiếu giả định

Tám tệp CSV mỗi tệp chứa sáu số đo kích thước gốc và
giả định. Mỗi dòng chứa `coupon`, `cible_um` và `mesure_um`. Đơn vị
là micromet nguyên. Không có số liệu nào đến từ một máy
sản xuất hay từ khách hàng. Với mỗi lô, quá trình xử lý tính số
phiếu, tổng và giá trị lớn nhất của các độ lệch tuyệt đối, và số độ lệch
nhỏ hơn hoặc bằng 15 µm. Ngưỡng này là một quy tắc của bài tập, không phải một
dung sai công nghiệp được khuyến nghị.

Kết quả chứa số lô, phiên bản công thức và dấu vân tay
SHA-256 của đầu vào. Việc dùng số nguyên cho phép ở đây một so sánh chính xác.
Một khối lượng công việc GPU với các phép tính dấu phẩy động sẽ cần quy tắc
so sánh riêng và các dung sai được biện minh.

## Đọc các trạng thái

| Trạng thái trong sổ đăng ký | Ý nghĩa trong bài tập này | Xử lý khi tiếp tục |
|---|---|---|
| `termine` | Kết quả được chấp nhận và trạng thái được ghi trong cùng một giao dịch | Kiểm tra; không tính lại |
| `en_cours` | Lần thử đã khai báo, chưa có kết quả nào được chấp nhận | Xem xét quyền cũ rồi cho phép tiếp tục |
| `a_reprendre` | Lô chưa bắt đầu hoặc lần thử đã được đưa lại vào hàng đợi một cách rõ ràng | Thực hiện một lần thử |

Sau khi ngắt được chèn vào LOT-004: LOT-001 đến LOT-003 đã hoàn thành,
LOT-004 đang xử lý, LOT-005 đến LOT-008 cần tiếp tục. Không có kết quả nào của
LOT-004 được chấp nhận. Sau `preparer-reprise`, còn lại ba lô hoàn thành và
năm lô cần tiếp tục. Cuối cùng, tám kết quả phải bằng với các kết quả
của bản tham chiếu. Chỉ LOT-004 có hai lần thử.

## Thực hiện lại việc chuyển đổi từng bước

Các đường dẫn sau đây tương đối so với thư mục chứa `exercice.py`.
Việc ngắt là một ngoại lệ được chèn vào sau khi tính toán và trước khi chấp nhận;
mã 75 ở bước này là dự kiến. Tiến trình dừng lại và giải phóng
khóa của nó. Đây không phải là một lần tắt máy đột ngột của máy tính hay một sự cố mất điện.

```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` lấy khóa cục bộ, kiểm tra tập dữ liệu, đóng nguồn, đóng
các kết nối SQLite và sao chép thư mục. Bản sao vẫn bị đóng cho đến
khi kết thúc việc kiểm tra nó; chỉ sau đó đích mới được phép sản xuất.
Một lệnh thực thi chạy trên nguồn đã đóng trả về mã 2
với `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.
