# Capordia — failover and resume exercise, version 1.0.0

This CPU exercise compares continuous processing with interrupted processing,
copied into another folder and then resumed in a new process. It shows which
batches are finished, in progress, and to be resumed. It does not measure
any GPU and does not demonstrate uninterrupted migration.

## Prepare the exercise

Use Python 3.10 or later with the standard `sqlite3` and
`hashlib` modules, on a writable local disk. No package, network access,
account, secret, or administrator is required. Extract all files
from the folder, then open a terminal in that folder. If the system uses
`python3` or `py -3`, replace `python` in the commands. The script and the
input set remain separate from the working folders it creates.

Do not run this exercise in a production folder or on a network
share. Choose a working folder name that does not exist: the program
refuses to reuse or overwrite an existing folder.

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

This command creates `essai-capordia/rapport-verification.json` and the folders
for each scenario. It exits with code 0 only if all
checks pass. Run it again with a new name for a new trial.
The command does not delete any previous trial.

## Our example: eight batches of fictitious coupons

The eight CSV files each contain six original and fictitious dimensional
measurements. Each row contains `coupon`, `cible_um`, and `mesure_um`. The units
are whole micrometers. No number comes from a production machine or
a client. For each batch, the processing calculates the number
of coupons, the sum and maximum of absolute deviations, and the number of deviations
less than or equal to 15 µm. This threshold is an exercise rule, not a
recommended industrial tolerance.

The result contains the batch number, the recipe version, and the
SHA-256 hash of the input. Using integers here allows an exact comparison.
A GPU workload with floating-point calculations would require its own
comparison rule and justified tolerances.

## Reading the states

| State in the registry | Meaning in this exercise | Action on resume |
|---|---|---|
| `termine` | Result accepted and state recorded in a single transaction | Verify; do not recalculate |
| `en_cours` | Attempt declared, no result accepted | Examine the previous authority, then authorize the resume |
| `a_reprendre` | Batch not started or attempt explicitly requeued | Run an attempt |

After the injected cut on LOT-004: LOT-001 to LOT-003 are finished,
LOT-004 is in progress, LOT-005 to LOT-008 are to be resumed. No result for
LOT-004 is accepted. After `preparer-reprise`, three remain finished and
five are to be resumed. At the end, all eight results must be equal to those
of the reference. Only LOT-004 has two attempts.

## Redoing the failover step by step

The following paths are relative to the folder containing `exercice.py`.
The cut is an exception injected after the calculation and before acceptance;
code 75 at this stage is expected. The process stops and releases its
lock. This is neither a hard shutdown of the computer nor a power failure.

```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` takes the local lock, checks the set, closes the source, closes
the SQLite connections, and copies the folder. The copy remains closed until
its verification is complete; only the target is then allowed to produce.
A run command launched on the closed source returns code 2
with `SOURCE_CLOTUREE`.

The comparison command requires eight accepted results on each side,
verifies their inputs and outputs, then compares a canonical JSON
serialization of the results. Text exports can be rebuilt from
SQLite: they are not the authority that decides whether a result is accepted.

## The files produced

| File | Role |
|---|---|
| `registre.sqlite` | Reference state, attempts, owner, closure and accepted results |
| `manifeste.json` and `donnees/` | Frozen set of inputs with the expected fingerprints |
| `etat.json` | Readable state and tally of the three statuses |
| `registre-lots.csv` | Export: id, etat, tentatives, entree_sha256, sortie_sha256 |
| `resultats.json` | Export of the accepted results, in order of identifiers |
| `rapport-verification.json` | In the global demonstration folder: checks, environment and command outputs |
| `worker.lock` | Temporary lock of our program, removed on its normal exit or on the injected cutoff |

The `registre-lots-vierge.csv` file provided in the resource is a broader
template for your team. It adds owner, authorized machine, acceptance
criterion and reason for resumption. It does not replace the exercise's
database and is not imported by the program.

## The errors verified

The demonstration also tests re-running a completed job, rejecting a
second acceptance of the same batch, a modified input, a duplicated
identifier in the manifest, an altered output, a lock already present and an
exception inside the acceptance transaction. This last case leaves three
accepted outputs, not four, then finds the eight results again after resumption.
Failover is tested by copying the recent state of the target into a third
folder and closing the target. The old snapshot is not reactivated.

The exit codes are: 0 success; 2 expected business refusal; 75 expected
injected cutoff; 1 read or database error. The global scenario explicitly
expects the refusals it causes. Any other error fails the test.

## Limits to keep with the results

A single worker takes part in this exercise. The lock and the closure field
work in the local folders handled by this program. They do not prevent a
person from copying the database or another program from ignoring the lock.
This is not distributed fencing. Before a real failover, the old machine must
lose its right to publish through an independent, tested mechanism. If its
shutdown is not confirmed, a second authority is not started.

A transaction here groups result and status in **the same local database**.
It does not cover a send to an external system, a notification or a
file already consumed elsewhere. The absence of double acceptance verified
is limited to this database and this exercise. SHA-256 here detects a discrepancy
between bytes and the expected fingerprint; a manifest that can be modified along with the data
does not authenticate their origin.

If a process is killed abruptly, the lock may remain present. Verify
its actual shutdown before any manual intervention; the exercise never
automatically removes a lock deemed old. An interrupted copy
leaves the source closed: keep the folders and examine the incident,
without putting two copies back into activity. Power loss, network
file systems, cross-machine concurrency, GPU drivers,
framework checkpoints and performance are not qualified here.

## Result of our check

On September 24, 2026, under Windows AMD64, Python 3.14.6 and SQLite 3.50.4:
16 checks passed, 37 commands executed, 8 accepted results identical
after failover. The evidence available for review is `preuve-execution-cpu.json`.
The fingerprint of the canonical results is
`bde1bc833092cc8ca481cda5f44cfef36f14f0a94dff3f856624e169f5456660`.
These figures describe this set of 48 rows and are not a GPU benchmark.

See [PROCEDURE.md](PROCEDURE.md) for responsibilities and failover,
[SOURCES.md](SOURCES.md) for primary sources, and [LICENSE.txt](LICENSE.txt)
for the terms of reuse.
