From 6953dd3f7590b4d0550017bd6f0bff56ae17d6c1 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Fri, 9 Oct 2026 06:58:06 +0200 Subject: [PATCH 01/12] =?UTF-8?q?chore(story/15-13b-sauvegarde-persistante?= =?UTF-8?q?):=20planification=20report=C3=A9e=20sur=20main=20(fiches,=20pr?= =?UTF-8?q?ompts,=20registre=20par=20union)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .../15-13-mariadb-et-sauvegarde.md | 6 + ...13b-sauvegarde-avant-import-persistante.md | 240 ++++++++++++++---- .../15-13b-validate-prompt-p6.md | 35 +++ .../epic-15-choix-autonomes.md | 23 ++ 4 files changed, 254 insertions(+), 50 deletions(-) create mode 100644 _bmad-output/implementation-artifacts/15-13b-validate-prompt-p6.md diff --git a/_bmad-output/implementation-artifacts/15-13-mariadb-et-sauvegarde.md b/_bmad-output/implementation-artifacts/15-13-mariadb-et-sauvegarde.md index 66fd687d..e0703ae5 100644 --- a/_bmad-output/implementation-artifacts/15-13-mariadb-et-sauvegarde.md +++ b/_bmad-output/implementation-artifacts/15-13-mariadb-et-sauvegarde.md @@ -181,3 +181,9 @@ les tableaux). fille : 15-13a **1 MEDIUM** distinct (R4-1 = F1), 15-13b **4 MEDIUM** distincts (R4-2 = F-P4-1) ; 0 CRITICAL/HIGH. Remédiation dans chaque fille (Change Log) ; choix C-15-13-21 à C-15-13-26. Ici : ligne « Inventaire » du recompte réduite aux totaux (R4-7). +- 2026-10-09 — **Validation P5 de la 15-13b** (agent remédiateur, Opus 5.5). Deux lentilles **Opus 5.5** : + **2 MEDIUM** distincts (R5-1, né de la remédiation P4 ; F-P5-1, d'origine P1), 13 LOW (12 distincts : R5-3 = F-P5-8) ; 0 CRITICAL/HIGH. + Trend de la 15-13b : P4 4 → P5 2. Remédiation dans la fiche fille (Change Log) ; choix C-15-13-27 à + C-15-13-29. Ici : union des mutations des deux filles portée à **51** (M1 à M51 : la 15-13b ajoute M47 à + M51) ; la phrase « deux gestes » de `CHANGELOG.md:46` est **partagée** entre les deux filles (R5-5, + ventilation écrite dans la 15-13b seule). diff --git a/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md b/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md index 860079f3..cb2279e3 100644 --- a/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md +++ b/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md @@ -8,7 +8,7 @@ Status: ready-for-dev aux deux bornes : fiche index, § « Découpage ») ; la remédiation de la validation P3 y est appliquée. **La numérotation de la fiche unique est conservée** (AC, tests, mutations, tâches) pour la traçabilité : les numéros absents vivent dans la 15-13a. Choix : C-15-13-3, -4, -6, -8, -9, -14, -16, -18, - -19, -20, -21, -24, -25 et -26 du registre `epic-15-choix-autonomes.md`. Statut `ready-for-dev` : convention du registre pour une + -19, -20, -21, -24, -25, -26, -27, -28 et -29 du registre `epic-15-choix-autonomes.md`. Statut `ready-for-dev` : convention du registre pour une fiche en cours de validation. --> **Issues** : **ferme #552 et #576**. La PR porte `closes #552` **et** `closes #576` (mots-clés dans le titre @@ -112,7 +112,9 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* défaut `/data/backup`, monté par les compose distribués sur `./backup` ; **hors Docker**, poser la variable sur un dossier inscriptible (sinon l'import est refusé avant toute suppression, la création du dossier échouant, avec le message de l'AC 15). Les recettes du dépôt qui lancent un backend hors - Docker posent la variable (AC 16). + Docker posent la variable (AC 16). Le doc-comment du test 6 a (`config.rs:1643-1646`, « `/tmp` (et non + un chemin vide, qui ferait écrire le backup pré-import dans le répertoire courant) ») devient faux avec + le défaut neuf : il nomme `/data/backup` (R5-7 de la validation P5). (c) Les constructeurs de test (`config.rs:508`, `:1508`) **gardent** `/tmp` : ce sont des configurations de test, pas le défaut. @@ -137,8 +139,9 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* (d) `docker-compose.dev.yml` n'est **pas** modifié (pile de développement non distribuée ; le conteneur y crée `/data/backup` dans son système de fichiers — comportement de développement assumé). (e) **Les dossiers d'hôte montés par défaut restent hors du dépôt et du contexte de build.** - `docker compose up` se lance depuis un clone (`DOCKER_START.md:27`, `:37`) : `./backup` y est créé au - premier import, et un `git add -A` y versionnerait une sauvegarde complète — un **secret**. Le symptôme + `docker compose up` se lance depuis un clone (`DOCKER_START.md:27`, `:37`) : `./backup` y est créé (vide) + par Docker au premier `docker compose up` — la forme courte d'un montage fait créer la source absente + par le démon —, rempli au premier import (F-P5-5 de la validation P5), et un `git add -A` y versionnerait une sauvegarde complète — un **secret**. Le symptôme vaut pour chaque source de montage par défaut, pas pour `./backup` seul : `./inbox` et `./documents` (justificatifs) ne sont pas ignorés non plus (`git check-ignore` muet, seul `log/` l'est, `.gitignore:34`). Donc, **pour chaque entrée de `MONTAGES`** (sources `./log`, `./inbox`, `./documents`, `./backup`) : @@ -172,10 +175,15 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* handler à un `.await`), OOM, `docker stop` — ne laisse donc qu'un `.partial`, **jamais un fichier nommé comme une sauvegarde**. **Refus d'écraser** : `rename` remplace une cible existante sur Unix ; la fonction vérifie donc, avant le renommage, que `` n'existe pas (`tokio::fs::try_exists`) et, sinon, - échoue (`.partial` supprimé au mieux). La fenêtre entre la vérification et le renommage n'est pas + échoue (`.partial` supprimé au mieux) ; un `try_exists` qui rend `Err` (droits sur le dossier) vaut + **échec**, jamais « absent » — le traiter comme absent rouvrirait l'écrasement (R5-3/F-P5-8 de la + validation P5). La fenêtre entre la vérification et le renommage n'est pas fermée — le nom est unique par construction (horodatage, pid, compteur) et un second écrivain du même nom serait déjà un défaut : angle mort écrit. Le journal `info` « backup pré-import écrit » suit le renommage - réussi. Si `write_all`, `sync_all` ou `rename` échoue, le `.partial` est supprimé au mieux + réussi. **On ne supprime que ce qu'on a créé** (F-P5-3) : un échec de `create_new` — `.partial` déjà présent, + écriture interrompue ou autre écrivain — ne supprime **rien**, et ce `.partial` préexistant garde son + contenu. Si `write_all`, `sync_all`, la vérification du nom final ou `rename` échoue, le `.partial` **créé + par l'appel** est supprimé au mieux (`remove_file`, échec journalisé en `warn!` sans masquer l'erreur d'origine) ; si `remove_file` échoue à son tour, le `warn!` **nomme le chemin** du `.partial` et dit qu'il n'est pas une sauvegarde valide. Dans tous ces cas, le message de l'AC 15 c (« aucune sauvegarde n'a été créée ») reste **vrai** : aucun @@ -184,9 +192,13 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* d'écraser et la variante d'erreur. (b) Le dossier, quand Kesh le crée, l'est en `0o700` (`tokio::fs::DirBuilder`, `recursive(true)`, `mode(0o700)` sous `#[cfg(unix)]`) ; un dossier **existant** (le montage de l'hôte) n'est pas modifié. - (c) Une erreur rend `AppError::AdminPreImportBackupFailed` (AC 15) avec le détail actuel - (« création répertoire backup '…' : … », « écriture backup '…' : … ») : aucun import sans sauvegarde - réussie (DC5 inchangé). + (c) Une erreur rend `AppError::AdminPreImportBackupFailed` (AC 15) avec un détail **par étape**, qui + nomme le chemin (R5-3/F-P5-8 de la validation P5 : le journal est le seul diagnostic, le corps n'expose + rien) — « création répertoire backup '…' : … » (inchangé), « création fichier partiel '…' : … » + (`create_new`), « écriture backup '…' : … » (`write_all`), « synchronisation backup '…' : … » + (`sync_all`), « vérification nom final '…' : … » (`try_exists` en `Err`), « nom final déjà présent + '…' » (refus d'écraser), « renommage backup '…' → '…' : … » (`rename`) : aucun import sans sauvegarde + réussie (DC5 inchangé). Les libellés sont du journal, non testés à la lettre. (d) Le commentaire `routes/admin.rs:475-477` (« le backup pré-import est pris avant le verrou FOR UPDATE ») est **faux** — il est pris sous le verrou (`:226-261`) — et corrigé au passage. @@ -198,10 +210,15 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* le contrôle de `docker-compose.yml` exige l'**égalité** quand la source attendue ne commence pas par `${` (aujourd'hui `starts_with` : `./backups` passerait) — les trois entrées existantes gardent le préfixe `${KESH_…_HOST_DIR:-`. Le message de refus pour `docker-compose.prod.yml` reste celui de #558. + Le message de refus pour `docker-compose.yml` (`configuration_transmise.rs:512-514`, « doit avoir pour + source `{prefixe_y}…}` ») est faux pour une source **exacte** — il imprimerait « `./backup…}` » : pour + une entrée dont la source attendue ne commence pas par `${`, le message devient « doit être exactement + `./backup` (montage fixe), trouvé `…` » (R5-6 de la validation P5) ; celui des trois entrées variables + est inchangé. **La comparaison est extraite en fonction pure** (F-P4-2 de la validation P4, C-15-13-24) : `source_conforme(compose: Compose, source: &str, attendue_y: &str, exacte_p: &str) -> bool` — pour `Y`, `starts_with(attendue_y)` si `attendue_y` commence par `${`, égalité sinon ; pour `P`, égalité —, que la - boucle de `controle_transmission` (`configuration_transmise.rs:509-519`) appelle. Sans elle, la règle + boucle de `controle_transmission` (`configuration_transmise.rs:509-521`) appelle. Sans elle, la règle d'égalité vit dans une boucle que l'auto-test (S) n'atteint pas, et rien ne la garde : les vrais compose portent `./backup`, que `starts_with("./backup")` accepte aussi. **Lien avec le défaut du code** (R4-1, C-15-13-24) : le test `transmission` exige en outre qu'une @@ -228,7 +245,11 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* tableaux de `sec:env-vars` restent sans `Overfull \hbox`** (acquis de la 15-11a). Le paragraphe du mode `cargo run` natif (`:194`, « Mode `cargo run` natif (hors Docker) sur Linux non-root », qui pose `KESH_PORT`) gagne une incise : hors Docker, `KESH_ADMIN_BACKUP_DIR` sur un dossier inscriptible, sans - quoi tout import est refusé (AC 15). + quoi tout import est refusé (AC 15). **Aucune de ces incises (`:757`, `:194`) ne donne d'exemple sous + `/tmp`** (R5-4 de la validation P5) : à moins de 90 caractères de `BACKUP_DIR`, il ferait rougir le + contrôle négatif `BACKUP_DIR.{0,90}/tmp` de l'AC 11 l, qui vise l'ancien défaut et non un exemple — et + l'exemple naturel à recopier, `/tmp/kesh-e2e/backup` des recettes de `docs/testing.md` (AC 16 b), est + précisément celui-là. Un exemple, s'il en faut un, se prend hors de `/tmp` (`$HOME/kesh-backup`). (e) **`sec:backup-ui-keshbackup`** (`:1664`, `:1667-1672`, `:1699`) : la sauvegarde de sécurité est dans le dossier `backup/` du répertoire du compose (`/volume1/docker/kesh/backup` sur Synology), nommée `kesh-pre-import--….keshbackup`, **créée en mode `0600`, propriétaire `root`** (le @@ -237,20 +258,26 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* depuis elle** (C-15-13-8) : l'import se fait par le navigateur, et ni File Station, ni un partage SMB, ni un `scp` sans `sudo` ne lisent un fichier `root` en `0600` ; le manuel donne donc le geste de rapatriement, en SSH : - `sudo cp backup/kesh-pre-import-<…>.keshbackup /volume1//`, puis - `sudo chown /volume1//kesh-pre-import-<…>.keshbackup`, l'import par l'écran, + `sudo cp backup/kesh-pre-import-<...>.keshbackup /volume1//`, puis + `sudo chown /volume1//kesh-pre-import-<...>.keshbackup`, l'import par l'écran, puis **la suppression de la copie** (secret) — geste **rejoué au T0** sur un conteneur jetable. La liste « L'import refuse le fichier et préserve l'installation si » (titre `:1667`, liste `:1668-1672` — **pas** `:1676-1690`, qui est la liste « Trois propriétés » des reprises de données) gagne l'entrée de - l'AC 15 : la sauvegarde de sécurité ne peut pas être écrite (dossier non inscriptible, disque plein) - — le message le dit, aucune sauvegarde n'est créée, rien n'est supprimé. Le défaut `/tmp` disparaît + l'AC 15 : la sauvegarde de sécurité ne peut pas être prise ni écrite — **deux pistes**, comme le + message (AC 15 c, F-P5-7 de la validation P5) : le dossier de sauvegarde (non inscriptible, disque + plein) ou la base de données (momentanément inaccessible) — le message le dit, aucune sauvegarde n'est + créée, rien n'est supprimé. Le défaut `/tmp` disparaît de `:1664` et de `:1699`. **Fichier `.partial`** (AC 9 a, C-15-13-21) : le manuel dit qu'un `kesh-pre-import-….keshbackup.partial` est une écriture interrompue, **pas** une sauvegarde, et qu'il peut être supprimé. **Forme du rapatriement** (F-P4-8) : le bloc `sudo cp` / `sudo chown` est introduit par « depuis le répertoire du compose » (le chemin `backup/…` est relatif) et posé dans un `lstlisting` ordinaire (76 caractères), **pas** dans un `keshwarning` (70) : la ligne - `sudo chown /volume1//kesh-pre-import-<…>.keshbackup` en mesure 74 ; si la mise - en page l'exige ailleurs, elle est coupée par `\`. **Ouverture de la section** (`:1643`, « sans accès + `sudo chown /volume1//kesh-pre-import-<...>.keshbackup` en mesure **76** + (`printf '%s' … | wc -m`, remesuré en remédiation P5 avec `<...>`), soit la limite exacte ; si la mise + en page l'exige, elle est coupée par `\`. **Le bloc est en ASCII** (F-P5-6 de la validation P5) : `<...>` + et non `<…>` (U+2026) — aucun `lstlisting` du manuel ne porte de caractère hors ASCII, et `listings` + sous XeLaTeX en place mal certains ; la prose, elle, garde `…`. Contrôle aplati positif ajouté à l'AC 11 l + pour la ligne entière. **Ouverture de la section** (`:1643`, « sans accès SSH ni ligne de commande ») : vraie pour l'export et l'import par l'écran, mais la section reçoit désormais un geste en SSH ; une phrase la précise — la sauvegarde de sécurité **pré-import** fait exception, son rapatriement demande un accès SSH (voir plus bas) — (F-P4-7). Le tableau `:1712` @@ -292,7 +319,12 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* fois au PDF d'avant et ne discrimine pas) ; tous vérifiés **absents** du PDF aplati à `468ce610` (`target/gate-logs/15-13-p1-R-admin-plat.txt`) en remédiation P1 ; `keshbackup.partial` et `target: /data/backup` (vérifiés absents, en remédiation P4, de - `target/gate-logs/15-13b-p4-F-admin-manual-plat.txt` : `grep -c partial` → 0) ; **négatifs** — + `target/gate-logs/15-13b-p4-F-admin-manual-plat.txt` : `grep -c partial` → 0) ; la ligne `sudo chown` + **entière**, en ASCII (`grep -cF 'sudo chown /volume1//kesh-pre-import-<...>.keshbackup'` + → 1 : elle vérifie le rendu du bloc par `listings`, F-P5-6) et l'entrée de la liste des refus qui nomme + la base (`grep -cE 'base de données.{0,40}inaccessible'` ≥ 1, F-P5-7) — les deux vérifiés absents, en + remédiation P5, de `target/gate-logs/15-13b-p5-F-admin-plat.txt` (`grep -cF 'kesh-pre-import-<...>'` + → 0 ; le motif de la base → 0) ; **négatifs** — `grep -E 'BACKUP_DIR.{0,90}/tmp'` vide (la cellule du tableau, `:757`, que `défaut /tmp` ne voit pas) ; `défaut /tmp` absent ; `deux gestes` absent (présent **une** fois au PDF d'avant, relevé en validation P3 — discriminant). Au `.log`, aucun `Overfull \hbox` **nouveau** (comparé au `.log` d'avant, relevé au @@ -305,8 +337,12 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* de la brochure est régénéré par le même `make fr`. 12. **`DOCKER_START.md`** (partie sauvegarde). - (a') Une ligne sur le dossier `./backup` : sauvegarde de sécurité prise avant un import, **secret**, - ignorée de git (AC 8 e), à purger. *(Le reste de l'AC 12 — prérequis, étape `.env`, accès à la base, + (a') Une ligne sur le dossier `./backup` : créé par Docker au premier `docker compose up`, il reçoit la + sauvegarde de sécurité prise avant chaque import ; **secret**, ignoré de git (AC 8 e), à purger. + **Emplacement** (F-P5-4 de la validation P5) : le fichier n'a pas de section « dossiers » et ne nomme + aucun montage (`grep -n "inbox\|documents\|log/\|backup" DOCKER_START.md` → vide) ; la ligne est une puce + de plus à la fin de § *Notes* (`DOCKER_START.md:140`, après celle du volume `mariadb_data`, que la + 15-13a peut retoucher : conflit d'une ligne au rebase, sans autre effet). *(Le reste de l'AC 12 — prérequis, étape `.env`, accès à la base, volume, « Base de données ne s'initialise pas », retrait d'`init-demo.sh` — est dans la 15-13a.)* 13. **CHANGELOG `[0.13.0]`** (parties sauvegarde). @@ -316,13 +352,20 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* le dit, au lieu d'annoncer une sauvegarde qui n'existe pas. Rubrique **Sécurité** : les dossiers montés par défaut (`backup`, `inbox`, `documents`, `log`) sont ignorés de git et du contexte de build (AC 8 e). *(La rubrique **Retiré** `init-demo.sh` : 15-13a.)* - (c) **Propagation** : `CHANGELOG.md:46` porte **cinq** phrases que la 15-13 rend fausses (F-P3-1) ; - **deux** relèvent de la 15-13b, réécrites : « — la sauvegarde pré-import reste dans `/tmp` » (avec le - compose de la 0.13.0, elle va dans `./backup` ; avec un compose gardé sans le montage, dans - `/data/backup` du conteneur, éphémère) ; « le manuel d'administration décrit les **deux gestes** » (le - décompte par fichier de l'AC 11 f — trois et trois, ou cinq et trois si la 15-13a est mergée — ou un - renvoi sans nombre). Les trois autres (refus de Compose, défaut sans avertissement des mots de passe, - `.env` copié du gabarit) sont de la 15-13a. + (c) **Propagation** : `CHANGELOG.md:46` porte **cinq** phrases que la 15-13 rend fausses (F-P3-1). + Ventilation (R5-5 de la validation P5 : la phrase des gestes était revendiquée par les deux fiches, + 4 + 2 = 6 ≠ 5) : **une** relève de la 15-13b seule ; **trois** de la 15-13a seule (refus de Compose, + défaut sans avertissement des mots de passe, `.env` copié du gabarit) ; **une est partagée**, « le + manuel d'administration décrit les **deux gestes** » — soit 1 + 3 + 1 partagée = 5. Les « quatre » + phrases de l'AC 13 c de la 15-13a comptent la partagée ; la fiche sœur n'est pas retouchée (close, en + développement), le décompte se lit ici. + — La phrase **propre** à la 15-13b, réécrite : « — la sauvegarde pré-import reste dans `/tmp` » (avec + le compose de la 0.13.0, elle va dans `./backup` ; avec un compose gardé sans le montage, dans + `/data/backup` du conteneur, éphémère). + — La phrase **partagée**, « deux gestes » : réécrite par la **première** des deux fiches mergée, + son décompte **refait** par la seconde au rebase (§ *Dépendances*) — le décompte par fichier de + l'AC 11 f (trois et trois avec la 15-13b seule, cinq et trois avec les deux) ou un renvoi sans nombre, + qui n'a alors rien à recompter. **Et `CHANGELOG.md:44`** (entrée #550 de `[0.13.0]`, F-P4-3 de la validation P4) : « **Inchangé** : avec `docker-compose.prod.yml`, les montages restent `./documents`, `./inbox`, `./log` » devient faux dans la même version — la phrase gagne « ; la 0.13.0 y ajoute `./backup` (sauvegarde pré-import, #552) ». @@ -338,8 +381,9 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* (a) Variante neuve `AppError::AdminPreImportBackupFailed(String)` (`errors.rs`) : HTTP 500, code `ADMIN_PRE_IMPORT_BACKUP_FAILED`, clé `error-admin-pre-import-backup-failed`, détail journalisé et jamais exposé (comme `AdminFullImportFailed`). Elle couvre **tout** échec antérieur à l'écriture - réussie du fichier : `routes/admin.rs:236`, `:240`, `:246` (transaction, verrou), `:473`, `:487` - (dossier, fichier — et `write_backup_file` de l'AC 9). Le doc-comment de `AdminFullImportFailed` + réussie du fichier : `routes/admin.rs:236`, `:240`, `:246` (transaction, verrou, ligne `_kesh_version` + absente), `:473`, `:487` (dossier, fichier — et `write_backup_file` de l'AC 9) ; avec les deux appels + de l'AC 15 b (`:172`, `:259`), **sept** sites antérieurs à l'écriture (R5-2 de la validation P5). Le doc-comment de `AdminFullImportFailed` (`errors.rs:448-452`, « backup pré-import impossible ») est corrigé : elle ne couvre plus que les échecs **postérieurs** à la sauvegarde (`admin.rs:283` à `:462`, douze sites, inchangés). (b) Les deux sites qui naissent **hors** du module — `check_schema_compat` (`admin_backup/import.rs:204`, @@ -348,13 +392,31 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* `routes/admin.rs`, par une fonction pure `avant_sauvegarde(AppError) -> AppError` qui change `AdminFullImportFailed` et `AdminFullExportFailed` en `AdminPreImportBackupFailed` et rend toute autre variante telle quelle (`ImportSchemaMismatch` reste un 400). `admin_backup` n'est pas modifié. + **Forme prescrite de l'appel** : `….await.map_err(avant_sauvegarde)?`, dans l'instruction même de + l'appel — pas de fermeture `|e| avant_sauvegarde(e)`, pas de conversion différée. **Le branchement est + gardé** (F-P5-1 de la validation P5, C-15-13-28) : les deux sources ne sont pas injectables à bon + compte (une table supprimée fait d'abord rougir `check_schema_compat` en 400), si bien que le test 14 + n'établit que la fonction pure ; le test 20, **lexical**, vérifie qu'elle est appliquée aux deux appels + de l'import, et **pas** à celui de l'export (`full_export`, `:39`, où un échec reste un échec + d'export). (c) Texte, **quatre locales** (`fr-CH`, `de-CH`, `en-CH`, `it-CH`) et **repli Rust** de `errors.rs` (rendu quand aucun catalogue n'est chargé) : l'import a échoué **avant** la sauvegarde de sécurité ou pendant son écriture ; **aucune sauvegarde n'a été créée** ; **rien n'a été supprimé** ; vérifier les journaux du serveur, puis réessayer — **sans orienter vers une seule cause** (F-P4-5 de la validation P4, - C-15-13-25) : sur les cinq sites antérieurs à l'écriture, trois sont des pannes de **base** (transaction, - verrou, lecture du schéma ou des données), deux du **dossier** ; le texte nomme donc les deux pistes, à - égalité (« dossier de sauvegarde inscriptible, base de données accessible »), ou n'en nomme aucune. La + C-15-13-25) : sur les **sept** sites antérieurs à l'écriture (AC 15 a-b, R5-2 de la validation P5), + **cinq** relèvent de la **base** (`:172` lecture du schéma, `:236` transaction, `:240` verrou, `:246` + ligne `_kesh_version` absente, `:259` lecture des données — et erreurs `zip`/`serde_json` de + `build_keshbackup`), **deux** du **dossier** (`:473`, `:487` devenu `write_backup_file`, avec ses étapes + `.partial`). **Le texte nomme toujours les deux pistes, à égalité** (R5-1 de la validation P5, + C-15-13-27, qui rectifie C-15-13-25 : l'option « n'en nommer aucune » est **retirée**) — le dossier de + sauvegarde (inscriptible ?) **et** la base de données (joignable ?) —, dans les quatre locales **et** le + repli Rust. C'est ce qui fonde l'écartement du contrôle du dossier au démarrage (angle mort, + C-15-13-9) : le message nomme les deux causes possibles au moment où elles comptent ; un texte qui n'en + nommerait aucune laisserait le cas nominal de #576 (instance hors Docker sans la variable) sans + piste, et l'écartement sans fondement. **Jetons des pistes, à la lettre** (vérifiés par les tests 15 + et 16) : `fr-CH` « dossier de sauvegarde » et « base de données » ; `de-CH` « Sicherungsordner » et + « Datenbank » ; `en-CH` « backup folder » et « database » ; `it-CH` « cartella di backup » et + « database » ; repli Rust : ceux de `fr-CH`. La **négation** est la forme attendue (« aucune sauvegarde n'a été créée », « Es wurde keine Sicherung erstellt », « No backup was created », « Non è stato creato alcun backup ») : le test 16 n'interdit pas les mots « créé » / @@ -367,7 +429,9 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* seulement l'absence de la promesse** (F-P4-4) : le test 16 vérifie que le texte de chaque locale **contient** sa négation (les quatre formulations ci-dessus, à la lettre) — un texte neuf « Échec de l'import. » ne dirait plus qu'aucune copie n'existe, ce qui est l'objet de #576 ; le test 15 vérifie de - même que le repli Rust contient « aucune sauvegarde n'a été créée ». + même que le repli Rust contient « aucune sauvegarde n'a été créée ». **Les deux pistes sont exigées de + même** (R5-1) : le test 16 vérifie que chaque texte **contient** ses deux jetons, le test 15 que le + repli Rust contient « dossier de sauvegarde » et « base de données » (mutations **M49**, **M50**). (d) Frontend **non touché** : un 500 affiche le `message` du corps (`AdminRestorePanel.svelte:82-84`), aucun code client ne lit `ADMIN_FULL_IMPORT_FAILED`. Le test de parité des catalogues (`kesh-i18n/src/loader.rs:729-800`) reste vert sans entrée neuve dans `dette-parite-connue.txt`. @@ -450,7 +514,7 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* - [ ] `routes/admin.rs` : `write_backup_file` (`.partial` en `create_new` + `0o600`, `sync_all`, refus si le nom final existe, `rename` ; `.partial` supprimé au mieux en cas d'échec) ; dossier créé en `0o700` ; commentaire `:475-477` corrigé ; sites antérieurs à la sauvegarde sur `AdminPreImportBackupFailed` ; `avant_sauvegarde` aux appels de - `check_schema_compat` et `build_keshbackup`. + `check_schema_compat` et `build_keshbackup` (forme `.map_err(avant_sauvegarde)`, AC 15 b, test 20). - [ ] `errors.rs` : variante `AdminPreImportBackupFailed`, son bras de réponse et son repli ; doc et repli d'`AdminFullImportFailed` (AC 15 a, c). - [ ] `crates/kesh-i18n/locales/{fr-CH,de-CH,en-CH,it-CH}/messages.ftl` : clé @@ -458,7 +522,7 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* - [ ] **T3 — Tests Rust** (AC 7, 8 e, 9, 10 c-e, 15) — voir § *Tests et mutations*. Dans `configuration_transmise.rs` : quatrième entrée de `MONTAGES`, fonction pure `source_conforme` (égalité exacte pour une source sans `${`), lien `DEFAULT_ADMIN_BACKUP_DIR` ↔ cible de `MONTAGES`, textes - « trois » mis à jour (AC 10 c, e) ; test 19 (AC 8 e). + « trois » mis à jour, message de refus `Y` d'une source exacte (AC 10 c, e) ; test 19 (AC 8 e). - [ ] **T5 — Manuel et brochure** (AC 11) : sites (d) (ligne `KESH_ADMIN_BACKUP_DIR`, montages, `:194`), (e), (f) (paragraphe sauvegarde, `:1773`, `:1793`), (i), puis relecture de la brochure (n) ; `make fr` (trois PDF), contrôles (k)–(l). @@ -503,25 +567,30 @@ Numérotation de la fiche unique conservée. | 5 | `s_sources_de_montage` (existant, complété) — exerce la fonction pure `source_conforme` (AC 10 d) : en `Y` avec `"./backup"`, `./backup` accepté et `./backups` **refusé** ; en `Y` avec un préfixe `${…:-`, la source variable acceptée ; en `P`, égalité | AC 10 c-d | **M14** `source_conforme` revient à `starts_with` pour toutes les entrées (le cas `./backups` en `Y` passe) | | 6 | `from_env_empty_or_blank_vars_take_code_default_silently` (existant, `config.rs:1648`, assertion par la constante — 6 a) + `from_env_absent_backup_dir_takes_data_backup` (nouveau — 6 b, assertion sur le **littéral** `"/data/backup"`, seul littéral voulu côté test : il fixe la valeur dont #552 dépend) | AC 7 | **M15** **valeur de la constante** `DEFAULT_ADMIN_BACKUP_DIR` changée en `"/tmp"` (geste naturel : 6 a, qui compare la constante à elle-même, reste vert ; 6 b et le test 3 rougissent) | | 11 | `write_pre_import_backup_en_0600` (nouveau, `#[cfg(unix)]`, `mod tests` neuf de `routes/admin.rs`, `tempfile`) — `write_pre_import_backup` sur un sous-dossier absent d'un `TempDir` : dossier créé en `0700`, fichier en `0600`, contenu identique ; **assertions de montage** : un fichier témoin écrit par `std::fs::write` dans le même dossier n'est **pas** en `0600`, **et** un dossier témoin créé par `std::fs::create_dir` à côté n'est **pas** en `0700` (R4-9 de la validation P4 : sans ce second témoin, M25 passe sous umask `077`) — sinon l'umask rend le test non discriminant : échec explicite, dont le message **nomme la cause** (« umask de l'environnement trop restrictif (077 ?) : le test ne peut pas distinguer le mode posé par le code »), et non un défaut du code ; sous l'umask `022` du poste de dev et de la CI, sans effet. Après l'écriture, aucun `.partial` ne reste dans le dossier | AC 9 a-b | **M24** `.mode(0o600)` retiré · **M25** `DirBuilder` sans `mode` (dossier en `0755`) | -| 12 | `write_backup_file_n_ecrase_pas` (nouveau, même module) — `write_backup_file` sur un chemin final **déjà occupé** échoue en `AppError::AdminPreImportBackupFailed`, le fichier existant garde son contenu, et aucun `.partial` ne reste ; **+** `write_backup_file_passe_par_un_partiel` (nouveau, même module, C-15-13-21) — un `.partial` **préexistant** (écriture interrompue) fait échouer l'appel en `AdminPreImportBackupFailed`, et le nom final **n'existe pas** après l'échec : le nom de la sauvegarde n'apparaît qu'au terme d'une écriture complète passée par le `.partial` | AC 9 a, 9 c | **M26** vérification d'existence du nom final retirée (le `rename` écrase) · **M36** `write_backup_file` rend `AdminFullImportFailed` · **M46** écrire directement le nom final, sans `.partial` (le second test rougit : l'appel réussit, le nom final existe) | +| 12 | `write_backup_file_n_ecrase_pas` (nouveau, même module) — `write_backup_file` sur un chemin final **déjà occupé** échoue en `AppError::AdminPreImportBackupFailed`, le fichier existant garde son contenu, et aucun `.partial` ne reste ; **+** `write_backup_file_passe_par_un_partiel` (nouveau, même module, C-15-13-21) — un `.partial` **préexistant** (écriture interrompue) fait échouer l'appel en `AdminPreImportBackupFailed`, le nom final **n'existe pas** après l'échec, et ce `.partial` préexistant **garde son contenu** (on ne supprime que ce qu'on a créé, F-P5-3 de la validation P5) : le nom de la sauvegarde n'apparaît qu'au terme d'une écriture complète passée par le `.partial` | AC 9 a, 9 c | **M26** vérification d'existence du nom final retirée (le `rename` écrase) · **M36** `write_backup_file` rend `AdminFullImportFailed` · **M46** écrire directement le nom final, sans `.partial` (le second test rougit : l'appel réussit, le nom final existe) · **M51** nettoyage du `.partial` étendu à l'échec de `create_new` (le second test rougit : le `.partial` préexistant a disparu) | | 14 | `avant_sauvegarde_convertit_les_echecs_anterieurs` (nouveau, unitaire, `routes/admin.rs`) — `AdminFullImportFailed` et `AdminFullExportFailed` → `AdminPreImportBackupFailed`, détail conservé ; `ImportSchemaMismatch` et `InvalidBackupStructure` rendus tels quels | AC 15 b | **M30** `avant_sauvegarde` rend l'erreur telle quelle · **M31** `avant_sauvegarde` convertit toute variante (un 400 devient un 500) | -| 15 | `pre_import_backup_failed_maps_to_500_without_promise` (nouveau, `errors.rs`, repli Rust — aucun catalogue n'est chargé dans les tests de la bibliothèque, `init_error_i18n` n'y est jamais appelé) — 500, `ADMIN_PRE_IMPORT_BACKUP_FAILED`, message sans le détail et sans promesse de sauvegarde, et qui **contient** « aucune sauvegarde n'a été créée » (F-P4-4) ; **témoin** : `AdminFullImportFailed` rend `ADMIN_FULL_IMPORT_FAILED` et un repli **différent** | AC 15 a, c | **M32** le bras de la variante neuve rend le code et la clé d'`AdminFullImportFailed` | -| 16 | `catalogues_distinguent_l_echec_de_sauvegarde` (nouveau, `errors.rs`, `I18nBundle::load` sur `concat!(env!("CARGO_MANIFEST_DIR"), "/../kesh-i18n/locales")` — chemin indépendant du répertoire courant (F-P3-10) —, **sans** toucher au catalogue global) — pour chacune des quatre locales : `error-admin-pre-import-backup-failed` ≠ `error-admin-full-import-failed` de la même locale ; hors `fr-CH`, ≠ le texte `fr-CH` (sinon `format` est retombé sur le français, `loader.rs:116-121`) ; ne contient pas la **promesse** de l'ancien texte, phrase intérieure de sa parenthèse **sans les parenthèses** (R3-9) : `un backup automatique a été créé avant l'opération`, `vor dem Vorgang wurde automatisch ein Backup erstellt`, `an automatic backup was created before the operation`, `prima dell'operazione è stato creato un backup automatico` — **pas** des sous-chaînes que la négation prescrite contient (`a été créé` est préfixe de « n'a été créée ») ; **assertion de montage** : chaque interdit figure dans `error-admin-full-import-failed` de **sa** locale (texte inchangé), faute de quoi l'interdit est une coquille et le test passe à vide. Vérifié en remédiation P3 : `grep -cF` de chaque interdit (sans parenthèses) → 1 dans le `messages.ftl` de sa locale (`fr-CH:1478`, `de-CH`/`en-CH`/`it-CH:1407`), et dans aucun autre ; `printf '%s' "" \| grep -cF -- ""` → 0 pour les vingt couples (quatre formulations de l'AC 15 c et « Non è stato creato un backup automatico », contre les quatre interdits) ; **et** (F-P4-4 de la validation P4) chaque texte neuf **contient** la négation de sa locale, à la lettre (`aucune sauvegarde n'a été créée`, `Es wurde keine Sicherung erstellt`, `No backup was created`, `Non è stato creato alcun backup`) | AC 15 c | **M33** texte `de-CH` remplacé par celui d'`error-admin-full-import-failed` · **M34** clé retirée d'`it-CH` (rouge aussi à `parity_between_locales`) · **M38** texte `fr-CH` neuf qui recopie la parenthèse de l'ancien · **M45** texte `fr-CH` neuf réduit à « Échec de l'import. » (sans négation ni promesse) | +| 15 | `pre_import_backup_failed_maps_to_500_without_promise` (nouveau, `errors.rs`, repli Rust — aucun catalogue n'est chargé dans les tests de la bibliothèque, `init_error_i18n` n'y est jamais appelé) — 500, `ADMIN_PRE_IMPORT_BACKUP_FAILED`, message sans le détail et sans promesse de sauvegarde, et qui **contient** « aucune sauvegarde n'a été créée » (F-P4-4) ainsi que les deux pistes, « dossier de sauvegarde » **et** « base de données » (R5-1 de la validation P5) ; **témoin** : `AdminFullImportFailed` rend `ADMIN_FULL_IMPORT_FAILED` et un repli **différent** | AC 15 a, c | **M32** le bras de la variante neuve rend le code et la clé d'`AdminFullImportFailed` · **M50** repli Rust de la variante neuve sans « base de données » (une seule piste) | +| 16 | `catalogues_distinguent_l_echec_de_sauvegarde` (nouveau, `errors.rs`, `I18nBundle::load` sur `concat!(env!("CARGO_MANIFEST_DIR"), "/../kesh-i18n/locales")` — chemin indépendant du répertoire courant (F-P3-10) —, **sans** toucher au catalogue global) — pour chacune des quatre locales : `error-admin-pre-import-backup-failed` ≠ `error-admin-full-import-failed` de la même locale ; hors `fr-CH`, ≠ le texte `fr-CH` (sinon `format` est retombé sur le français, `loader.rs:116-121`) ; ne contient pas la **promesse** de l'ancien texte, phrase intérieure de sa parenthèse **sans les parenthèses** (R3-9) : `un backup automatique a été créé avant l'opération`, `vor dem Vorgang wurde automatisch ein Backup erstellt`, `an automatic backup was created before the operation`, `prima dell'operazione è stato creato un backup automatico` — **pas** des sous-chaînes que la négation prescrite contient (`a été créé` est préfixe de « n'a été créée ») ; **assertion de montage** : chaque interdit figure dans `error-admin-full-import-failed` de **sa** locale (texte inchangé), faute de quoi l'interdit est une coquille et le test passe à vide. Vérifié en remédiation P3 : `grep -cF` de chaque interdit (sans parenthèses) → 1 dans le `messages.ftl` de sa locale (`fr-CH:1478`, `de-CH`/`en-CH`/`it-CH:1407`), et dans aucun autre ; `printf '%s' "" \| grep -cF -- ""` → 0 pour les vingt couples (quatre formulations de l'AC 15 c et « Non è stato creato un backup automatico », contre les quatre interdits) ; **et** (F-P4-4 de la validation P4) chaque texte neuf **contient** la négation de sa locale, à la lettre (`aucune sauvegarde n'a été créée`, `Es wurde keine Sicherung erstellt`, `No backup was created`, `Non è stato creato alcun backup`) ; **et** (R5-1 de la validation P5) chaque texte neuf **contient** ses deux jetons de piste, à la lettre (`dossier de sauvegarde`/`base de données`, `Sicherungsordner`/`Datenbank`, `backup folder`/`database`, `cartella di backup`/`database`) | AC 15 c | **M33** texte `de-CH` remplacé par celui d'`error-admin-full-import-failed` · **M34** clé retirée d'`it-CH` (rouge aussi à `parity_between_locales`) · **M38** texte `fr-CH` neuf qui recopie la parenthèse de l'ancien · **M45** texte `fr-CH` neuf réduit à « Échec de l'import. » (sans négation ni promesse) · **M49** texte `en-CH` neuf sans « database » (une seule piste, le dossier) | | 17 | `full_import_refuses_when_backup_cannot_be_written` (nouveau, `admin_full_import_e2e.rs`) — `admin_backup_dir` = chemin d'un **fichier** existant (la création du dossier échoue même sous root : en CI comme en local) ; `spawn_app` factorisé en `spawn_app_with(pool, config)` (DRY) ; 500, `ADMIN_PRE_IMPORT_BACKUP_FAILED`, message = repli (ce binaire n'appelle pas `init_error_i18n`) ; installation intacte (société et administrateur d'origine, comme le test 18) | AC 15 a, 9 c | **M35** `admin.rs:473` (création du dossier) rend `AdminFullImportFailed` | | 18 | `full_import_rolls_back_on_insert_failure` (existant, `admin_full_import_e2e.rs:559-605`, complété) — échec **après** la sauvegarde : code `ADMIN_FULL_IMPORT_FAILED` | AC 15 a | **M37** `admin.rs:291` (`restore :`) rend `AdminPreImportBackupFailed` | | 19 | `montages_hors_du_depot` (nouveau, `configuration_transmise.rs`) — pour **chaque** entrée de `MONTAGES`, le dossier de sa source `docker-compose.prod.yml` (`./x` → `x`) a sa ligne dans `.gitignore` sous la forme **ancrée** `/x/` — **seule tolérance** : `log`, pour qui `log/` (ligne existante, `.gitignore:34`) est admise, la liste des tolérances étant une constante qui ne compte que `log` (assertion) — et dans `.dockerignore` (`x/`) ; la liste est **dérivée** de `MONTAGES`, pas recopiée (un cinquième montage ajouté demain est contrôlé sans retouche) ; **assertion de montage** : `MONTAGES` compte **au moins** quatre entrées, dont celle de cible `/data/backup` (R4-5 de la validation P4, C-15-13-26 : un compte exact obligerait à retoucher le test au cinquième montage, contre la phrase précédente), et chaque ligne est cherchée **entière** (après `trim`), pas en sous-chaîne (`log/` ne doit pas satisfaire `/backup/`, ni `backup/` satisfaire `/backup/`) | AC 8 e | **M39** retirer `/backup/` de `.gitignore` · **M40** retirer `backup/` de `.dockerignore` · **M41** `/backup/` remplacé par `backup/` (non ancré) dans `.gitignore` (R3-1) | +| 20 | `avant_sauvegarde_branchee_aux_appels_de_l_import` (nouveau, `mod tests` de `routes/admin.rs`, F-P5-1 de la validation P5, C-15-13-28) — **garde lexicale** : lit `include_str!("admin.rs")` tronqué à la première ligne `#[cfg(test)]` (le module de test ne se lit pas lui-même), lignes dont le `trim_start` commence par `//` écartées (les doc-comments nomment `build_keshbackup` : `:129`, `:256`) ; repère chaque **appel** `check_schema_compat(` et `build_keshbackup(` (identifiant suivi de `(`, précédé ni de `fn ` ni d'un `use`), le rattache à la fonction de premier niveau qui l'entoure (dernière ligne `pub async fn ` / `async fn ` en colonne 0 qui le précède) et lit son **instruction** (texte jusqu'au `;` suivant, retours à la ligne compris : `rustfmt` peut la couper) — dans `full_import` et `run_backup_and_restore`, l'instruction **contient** `.map_err(avant_sauvegarde)` ; dans `full_export`, elle **ne le contient pas** ; **assertion de montage** : exactement **un** appel de `check_schema_compat` et **deux** de `build_keshbackup` (un par route), sinon échec qui nomme le décompte trouvé — un garde qui ne trouve rien passerait à vide, et un appel ajouté demain doit être trié | AC 15 b | **M47** `.map_err(avant_sauvegarde)` retiré à l'appel de `check_schema_compat` (`:172`) · **M48** retiré à l'appel de `build_keshbackup` (`:259`) | -**Décompte** (recompté sur le tableau) : 11 lignes, toutes côté Rust, qui portent **13 fonctions de test** -(9 neuves : lignes 6 b, 11, 12 ×2, 14, 15, 16, 17, 19 ; 4 existantes complétées : lignes 3, 5, 6 a, 18), dans +**Décompte** (recompté sur le tableau) : 12 lignes, toutes côté Rust, qui portent **14 fonctions de test** +(10 neuves : lignes 6 b, 11, 12 ×2, 14, 15, 16, 17, 19, 20 ; 4 existantes complétées : lignes 3, 5, 6 a, 18), dans cinq fichiers existants (`configuration_transmise.rs`, `config.rs`, `routes/admin.rs`, `errors.rs`, -`admin_full_import_e2e.rs`) ; **22 mutations** (M9–M11, M14, M15, M24–M26, M30–M41, M45, M46), toutes +`admin_full_import_e2e.rs`) ; **27 mutations** (M9–M11, M14, M15, M24–M26, M30–M41, M45–M51), toutes attendues rouges. M11 rougit **deux** familles ((T) et (F)), M15 et M34 deux tests chacune : attendu, le relever. **M26** change de sens avec l'écriture par `.partial` : `create_new` porte désormais sur le `.partial`, et c'est la vérification d'existence du nom final qui empêche l'écrasement. Non couverts par un test, écrits comme angles morts : les sites `admin.rs:236`, `:240`, `:246` (panne de base avant la -sauvegarde, non injectable sans abstraction) et la suppression du `.partial` de l'AC 9 a après un échec de -`write_all`, `sync_all` ou `rename` (non injectable) — y compris l'échec de cette suppression (`.partial` -laissé, nommé par le `warn!`). +sauvegarde, non injectable sans abstraction) ; les pannes **réelles** derrière `:172` et `:259` (lecture du +schéma, lecture des données — non injectables, F-P5-1) : leur **branchement** sur `avant_sauvegarde` est +gardé par le test 20 (M47, M48 rouges), mais aucun test n'en fait passer une panne effective jusqu'à la +réponse HTTP ; et la suppression du `.partial` de l'AC 9 a après un échec de +`write_all`, `sync_all`, de la vérification du nom final ou de `rename` (non injectable) — y compris l'échec +de cette suppression (`.partial` laissé, nommé par le `warn!`). En revanche, qu'un échec de `create_new` +**ne supprime pas** le `.partial` préexistant est vérifié par le test 12 (F-P5-3 de la validation P5). Le test **(L)** (`garde_lecture_du_code`) et **(E)** (`env_example`) doivent rester verts **sans modification** de leurs listes : la story n'ajoute aucune lecture d'environnement ni aucune variable. @@ -607,7 +676,7 @@ validation P3. **Résolu** = un AC le traite ; **assumé** = angle mort écrit. | grep (4) du T9, reste : `docker-compose.yml:127-143`, `docker-compose.prod.yml:112-168` (commentaires et montages un par un), `docker-compose.dev.yml:34`, `.env.example:216`, `:237`, `:244`, `:263`, `:266`, `CHANGELOG.md:46` (« revient à `./documents` »), `:674`, `crates/kesh-api/tests/configuration_transmise.rs:200-205`, `:367`, `docs/kesh-specifications.txt:1739`, `admin-manual.tex:819-821`, `:837`, `:853`, `:884`, `:898`, `:1421`, `:1516`, `:1555`, `:1778` | un montage nommé seul, ou une variable d'hôte — aucune énumération de **tous** les montages | **assumé** — restent vrais avec le quatrième (`:1421`, `:1555` : périmètre de sauvegarde, AC 11 i ; `:200-205` : `MONTAGES`, résolu par l'AC 10 c ; `:819-821` : tableau des variables d'hôte, sans ligne pour `./backup`, AC 11 d) | | `.github/workflows/release.yml:91-101` | lance l'image publiée (root, `/data/backup` inscriptible), sans import | **assumé** (AC 16) | | `.gitignore` (seul `log/`, `:34`), `.dockerignore` (aucun) | dossiers montés par défaut (`./backup`, `./inbox`, `./documents`, `./log`) non ignorés dans un clone | résolu (AC 8 e, test 19) ; `log/` non ancré : tolérance nommée | -| `frontend/src/routes/(app)/admin/backup/+page.svelte`, `+page.ts` (versionnés) | homonyme du dossier `backup` | **assumé** — protégé par l'ancrage `/backup/` (AC 8 e, M41) | +| `frontend/src/routes/(app)/admin/backup/+page.svelte`, `+page.ts` (versionnés) ; rendus par le jeton `backup/` du grep (1) (F-P5-2 de la validation P5) : `frontend/src/routes/(app)/admin/restore/+page.ts:1`, `frontend/src/lib/features/admin-backup/AdminBackupPanel.svelte:4`, `crates/kesh-db/migrations/20260915000001_audit_log_company_id.sql:22` | homonyme du dossier `backup` (route, commentaires ; la migration ne se modifie pas — P8) | **assumé** — protégé par l'ancrage `/backup/` (AC 8 e, M41) ; les trois derniers ne parlent pas du dossier de sauvegarde | | `crates/kesh-api/src/config.rs:250-256`, `:939-947`, `:1643-1661` | défaut `/tmp` | résolu (AC 7) | | `crates/kesh-api/src/config.rs:508`, `:1508` | `/tmp` des constructeurs de test | **assumé**, voulu (AC 7 c) | | `crates/kesh-api/src/config.rs:1586`, `:1601`, `:1748`, `:1769` | `KESH_ADMIN_BACKUP_DIR` comme variable témoin des tests (`reset_env`, `VIDE_EGALE_DEFAUT`, `env_nonempty`) | **assumé** — restent vrais, aucune valeur `/tmp` | @@ -615,7 +684,7 @@ validation P3. **Résolu** = un AC le traite ; **assumé** = angle mort écrit. | `crates/kesh-api/src/routes/admin.rs:475-477` | commentaire faux (« avant le verrou ») | résolu (AC 9 d) | | `crates/kesh-api/src/routes/admin.rs:42`, `:129-130`, `:174`, `:219`, `:227`, `:481`, `:492` | doc et journal « backup pré-import » | **assumé** — restent vrais (`:481` est le nom du fichier, inchangé) | | `crates/kesh-api/src/routes/admin.rs:467-470` | doc de `write_pre_import_backup` (« Échec d'écriture → 500 ») | résolu (AC 9 a : mode `0600`, refus d'écraser, variante d'erreur) | -| `crates/kesh-api/src/routes/admin.rs:236`, `:240`, `:246`, `:473`, `:487` ; `:172`, `:259` (appels) | échecs antérieurs à la sauvegarde | résolu (AC 15 a-b) | +| `crates/kesh-api/src/routes/admin.rs:236`, `:240`, `:246`, `:473`, `:487` ; `:172`, `:259` (appels) | échecs antérieurs à la sauvegarde (sept sites) | résolu (AC 15 a-b) ; branchement de `:172` et `:259` gardé par le test 20 ; `admin.rs:39` (`build_keshbackup` de l'**export**) **sans** `avant_sauvegarde`, voulu (test 20) | | `crates/kesh-api/src/routes/admin.rs:283` à `:462` (douze sites `AdminFullImportFailed`) | échecs postérieurs | **inchangé, voulu** (AC 15 a) | | `crates/kesh-api/src/errors.rs:448-452`, `:1819-1828` | variante et message de #576 | résolu (AC 15 a, c) | | `crates/kesh-api/src/admin_backup/import.rs:204`, `admin_backup/export.rs:103-105` | variantes nées hors du module | résolu **à l'appel** (AC 15 b) ; fichiers non modifiés | @@ -630,7 +699,7 @@ validation P3. **Résolu** = un AC le traite ; **assumé** = angle mort écrit. | `frontend/tests/e2e/password-recovery.spec.ts:16`, `frontend/tests/e2e/global-setup.ts:34` | `cargo run -p kesh-api` | **assumé** (AC 16 c) | | `DOCKER_START.md:131`, `.env.example:32`, `:186`, `:249`, `docker-compose.dev.yml:2`, `frontend/vite.config.ts:20`, `admin-manual.tex:668`, `:1315`, `docs/testing.md:61`, `CHANGELOG.md:655` | mentions du mode `cargo run`, sans recette de lancement (second grep de l'AC 16) | **assumé** (AC 16 c) | | `scripts/prepare-release.sh:109`, `crates/kesh-db/src/post_restore.rs:1168`, `crates/kesh-db/tests/test_schema_guard.rs:548`, `CLAUDE.md:798`, `docs/kesh-specifications.txt:1110` | `cargo run` d'autres binaires, ou prose | **assumé** (AC 16 c) | -| `DOCKER_START.md` — section « dossiers » (ligne neuve) | `./backup` | résolu (AC 12 a') | +| `DOCKER_START.md` § *Notes* (`:140`, puce neuve — le fichier n'a pas de section « dossiers ») | `./backup` | résolu (AC 12 a', F-P5-4) | | `admin-manual.tex:757`, `:807-822` (dont `:812`, énumération des montages fixes) | `sec:env-vars` (ligne `KESH_ADMIN_BACKUP_DIR`, montages) | résolu (AC 11 d, F-P4-3) | | `admin-manual.tex:1643` (« sans accès SSH ni ligne de commande », ouverture de `sec:backup-ui-keshbackup`) | contradiction avec le rapatriement en SSH de la même section | résolu (AC 11 e, F-P4-7 : phrase d'exception) | | `admin-manual.tex:1712` (« restauration self-service sans SSH »), `website/roadmap.html:231` (« SSH-free migration and restore ») | promesse de l'export/import par l'écran | **assumé** — vraie pour un `.keshbackup` exporté ; la sauvegarde pré-import en fait exception, dite à `:1643` (F-P4-7) | @@ -651,7 +720,13 @@ existante. `0600` ; non mesurées depuis le poste (AC 11 e). - **Échecs antérieurs à la sauvegarde sans test** : `routes/admin.rs:236`, `:240`, `:246` (panne de base avant le verrou) et la suppression du `.partial` de l'AC 9 a — non injectables sans abstraction ; - relus en revue, écrits ici. + relus en revue, écrits ici. **`:172` et `:259`** (F-P5-1 de la validation P5) : la panne réelle (lecture + du schéma, lecture des données, `zip`) n'est pas injectable non plus ; seul leur **branchement** sur + `avant_sauvegarde` est gardé, **lexicalement**, par le test 20 (M47, M48) — un garde qui lit le source + et non un comportement : une écriture que le test ne reconnaît pas (fermeture, conversion différée) + le fait rougir à tort, ce qui est voulu (forme prescrite à l'AC 15 b) ; aucun test ne fait traverser + une panne effective jusqu'à la réponse HTTP. L'extraction d'une fonction `sauvegarde_pre_import` + testable sur une base dégradée a été écartée (C-15-13-28). - **`.partial` non supprimé** : si l'écriture échoue après la création **et** que `remove_file` échoue à son tour — ou si le processus est arrêté pendant l'écriture —, un `….keshbackup.partial` reste dans le dossier de sauvegarde ; il ne porte pas le nom d'une sauvegarde, le message « aucune sauvegarde n'a été @@ -672,8 +747,11 @@ existante. - **Emplacement non communiqué à l'écran** : la réponse de l'import ne porte pas le nom du fichier (`backupCreated` seulement) ; écarté (C-15-13-6). - **Contrôle au démarrage du dossier de sauvegarde** (un `warn!` si `/data/backup` n'est pas - inscriptible) : écarté (C-15-13-9) — le message de l'AC 15 dit la cause au moment où elle compte, et les - recettes hors Docker posent la variable (AC 16). + inscriptible) : écarté (C-15-13-9, rectifié par C-15-13-27) — le message de l'AC 15 **nomme les deux + causes possibles**, le dossier de sauvegarde et la base de données, au moment où elles comptent (il ne + peut pas dire laquelle : le journal le dit), les tests 15 et 16 l'exigent (M49, M50), et les recettes + hors Docker posent la variable (AC 16). Ce fondement tient **tant que** le message nomme le dossier : + s'il cessait de le faire, l'écartement serait à rouvrir. - **Montage d'hôte `./backup` créé `root:root` `0755`** : l'exploitant qui le veut fermé fait `chmod 700 backup` (manuel) ; les fichiers sont `0600` de toute façon. @@ -792,3 +870,65 @@ converties à l'appel (AC 15 b), précisément pour ne pas ouvrir un module de p M24–M26, M30–M41, M45, M46 ; `sort -u` → 22) ; **3 lignes** au tableau des cas ; **41 lignes** d'inventaire ; **10 angles morts** ; **4 modules de code** (inchangé : `config`, `routes/admin`, `errors`, catalogues `kesh-i18n`). Union avec la 15-13a : 46 mutations, M1 à M46 sans trou ni doublon. +- 2026-10-09 — **Remédiation de la validation P5** (agent remédiateur, Opus 5.5, en autonomie). Passe P5 : + deux lentilles **Opus 5.5** en contexte frais (prompt `15-13b-validate-prompt-p5.md`) — R (regression + hunter) **0 CRITICAL / 0 HIGH / 1 MEDIUM / 6 LOW**, F (adversaire plein périmètre) **0 / 0 / 1 MEDIUM / + 7 LOW** ; aucun doublon MEDIUM — soit **2 MEDIUM distincts** ; LOW en partie communs (R5-3 = F-P5-8). + **Trend** (fiche unique puis 15-13b) : P1 (Opus 5.5 ×2) 8 MEDIUM distincts → P2 (Sonnet ×2) 6 → P3 + (Opus 5.5 ×2) 5 → découpage → P4 (Sonnet ×2) 4 → P5 (Opus 5.5 ×2) **2** ; 0 CRITICAL/HIGH aux cinq passes. + **Signal D5** (sévérité égale, MEDIUM → MEDIUM), déclaré au Project Lead via l'orchestrateur : + **R5-1 naît de la remédiation P4** (« ou n'en nomme aucune », ajouté par `3ebedca9` pour F-P4-5) — forme + de recyclage ; **F-P5-1** est d'origine (introduction d'`avant_sauvegarde`, remédiation P1 `07e168e1`). + Trois LOW naissent aussi de la P4 (R5-2, R5-3/F-P5-8, R5-4) et un de C-15-13-21 (F-P5-3). Décroissance + monotone, un défaut recyclé sur deux : pas de découpage proposé par le remédiateur — l'arbitrage revient + à l'orchestrateur. + - **MEDIUM** : **R5-1** (décision de l'orchestrateur, C-15-13-27) — le message d'échec antérieur à la + sauvegarde nomme **toujours** les deux pistes, dossier de sauvegarde et base de données, dans les quatre + locales et le repli Rust, par des jetons fixés à la lettre ; tests 15 et 16 complétés, **M49**, **M50** ; + angle mort « contrôle au démarrage » réécrit (le message nomme les deux causes) ; rectifie C-15-13-25 + (« ou aucune » retiré) et le motif de C-15-13-9. **F-P5-1** (décision de l'orchestrateur, tranchée par + le remédiateur, C-15-13-28) — un garde lexical **est** retenu : test 20 + `avant_sauvegarde_branchee_aux_appels_de_l_import` (lecture de `admin.rs`, appels de l'import convertis, + appel de l'export `:39` non converti, assertion de montage sur le nombre d'appels) ; forme d'appel + prescrite à l'AC 15 b ; **M47**, **M48** couvertes ; la panne réelle derrière `:172`/`:259` reste à + l'angle mort et à la phrase de clôture du tableau des tests. + - **LOW** : **R5-2** (sept sites antérieurs, cinq de base et deux de dossier, AC 15 a et c) ; + **R5-3/F-P5-8** (AC 9 a-c : un détail par étape ; `try_exists` en `Err` = échec ; C-15-13-29) ; + **R5-4** (AC 11 d : aucune incise ne donne d'exemple sous `/tmp`) ; **R5-5** (AC 13 c : « deux gestes » + **partagée**, ventilation 1 + 3 + 1 partagée = 5 ; la fiche 15-13a, close, n'est pas retouchée) ; + **R5-6** (AC 10 c : message « doit être exactement `./backup` » pour une source exacte) ; **R5-7** (AC 7 b : + doc-comment du test 6 a ; boucle `configuration_transmise.rs:509-521`, message `Y` `:512-514`, revérifiés) ; + **F-P5-2** (trois homonymes du jeton `backup/` à l'inventaire, ligne de l'homonyme) ; **F-P5-3** (AC 9 a + et test 12 : l'échec de `create_new` ne supprime rien, le `.partial` préexistant garde son contenu, + **M51** ; C-15-13-29) ; **F-P5-4** (AC 12 a' : puce à la fin de § *Notes*, `DOCKER_START.md:140` ; + inventaire) ; **F-P5-5** (AC 8 e : `./backup` créé par Docker au premier `docker compose up`, rempli au + premier import) ; **F-P5-6** (AC 11 e : bloc `lstlisting` en ASCII, `<...>` — ligne `sudo chown` + remesurée à **76**, la limite ; contrôle aplati positif de la ligne entière à l'AC 11 l) ; **F-P5-7** + (AC 11 e : l'entrée de la liste des refus nomme les deux pistes ; contrôle aplati positif à l'AC 11 l). + Les deux contrôles aplatis neufs vérifiés absents de `target/gate-logs/15-13b-p5-F-admin-plat.txt` + (`grep -cF` → 0 chacun). + - **Grep du symptôme par la valeur** (fiche) : `ou aucune`, `n'en nomme aucune`, `cinq (sites|échecs)`, + `<…>`, `\b74\b`, `509-519`, `premier import`, `dit la cause`, `section « dossiers »` — hors Change Log, ne + restent que des textes neufs et justes : « rempli au premier import » (AC 8 e), « et non `<…>` » + (AC 11 e), « n'a pas de section « dossiers » » (AC 12 a', inventaire). + - **Règles changées** : (1) **le texte de #576** nomme obligatoirement les deux pistes (test 15/16, M49, + M50) — l'option « aucune » disparaît ; (2) **l'appel d'`avant_sauvegarde`** a une forme prescrite, gardée + lexicalement (test 20) ; (3) **le nettoyage du `.partial`** ne porte que sur celui créé par l'appel, et + `try_exists` en erreur vaut échec. Les trois touchent la production : une passe P6 **complète** reste + justifiée, ou, si l'orchestrateur juge la convergence acquise, une passe ciblée sur ces trois points. + - **Recompte** (depuis cette fiche, `grep`) : **10 AC**, **9 tâches**, **12 lignes de test** portant + **14 fonctions Rust** (10 neuves, 4 existantes complétées) ; **27 mutations** (M9–M11, M14, M15, + M24–M26, M30–M41, M45–M51 ; `sort -u` → 27) ; **3 lignes** au tableau des cas ; **41 lignes** + d'inventaire (inchangé : lignes enrichies) ; **10 angles morts** (inchangé) ; **4 modules de code** + (inchangé). Union avec la 15-13a (24 mutations) : **51**, M1 à M51 sans trou ni doublon. + +- **2026-10-09 — validation P6 (Sonnet ×2, prompt `15-13b-validate-prompt-p6.md`)** : R 0 au-dessus de LOW + (4 LOW), F 0 au-dessus de LOW (8 LOW) ; axes exercés et non exercés déclarés par les deux lentilles. + **Validation close.** Trend (15-13 puis 15-13b) : P1 8 MEDIUM → P2 6 → P3 5 (découpage, C-15-13-16) → P4 4 + → P5 2 → P6 0. Modèles : Opus ×2, Sonnet ×2, Opus ×2, Sonnet ×2, Opus ×2, Sonnet ×2. Les 12 LOW de la P6 + (`target/gate-logs/15-13b-p6-{R,F}.md`) sont **à appliquer au T0 du développement** : R6-1 (justification + « aucun `lstlisting` hors ASCII » fausse : dix listings en portent), R6-2 (forme de l'AC 15 b sur plusieurs + lignes après rustfmt), R6-3 = F-P6-6 (`DirBuilder` récursif en `0700` applique le mode aux parents créés : + l'écrire), R6-4 (pas de `serde_json` dans `export.rs`), F-P6-1 (conflit certain avec la 15-13a sur + `CHANGELOG.md:46` et `admin-manual.tex:1793` : à écrire aux Dépendances), F-P6-2 (M52 : branche négative du + test 20), F-P6-3, F-P6-4 (`README.md:48`), F-P6-5, F-P6-7, F-P6-8 (« dossier » contre « répertoire »). diff --git a/_bmad-output/implementation-artifacts/15-13b-validate-prompt-p6.md b/_bmad-output/implementation-artifacts/15-13b-validate-prompt-p6.md new file mode 100644 index 00000000..77190126 --- /dev/null +++ b/_bmad-output/implementation-artifacts/15-13b-validate-prompt-p6.md @@ -0,0 +1,35 @@ +# Prompt — validation P6 de la spec, Story 15-13b + +*Versionné le 2026-10-09. Deux lentilles (Sonnet), contexte frais chacune, en lecture seule. Rotation (décision D6 de la +rétro 25) : passes complètes Sonnet ↔ Opus ; Haiku réservé à une passe ciblée de fin de boucle.* + +Dépôt `/home/gcorbaz/devel/kesh-15-13`, branche `story/15-13-mariadb-et-sauvegarde`. **Fiche** : `_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md`. La dernière remédiation est le commit `e590935f` (`git show e590935f`) — lis le Change Log de la fiche. +Issues, par `gh api repos/guycorbaz/kesh/issues/N` (et `/comments`) — `gh issue view` échoue ici : #552, #576. +Choix autonomes : `_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md`. Règles : `CLAUDE.md`. Code de référence : la branche elle-même (`de285ea8` + les fiches). Index `15-13-mariadb-et-sauvegarde.md`, sœur `15-13a-…` (close, en développement ailleurs : n'en signale rien sauf contradiction de frontière). Choix C-15-13-1 à 29. Passe 6 sur 8 : ne classe en MEDIUM que ce qui ferait livrer un comportement faux, un texte faux ou un test qui ne mord pas. **Axes prioritaires** : (1) le test 20 (garde lexicale `avant_sauvegarde`) — survit-il à rustfmt (forme `….await.map_err(avant_sauvegarde)?` sur plusieurs lignes) ? peut-il passer à vide ? distingue-t-il l'appel de l'export (`admin.rs:39`) ? ; (2) les mots des deux pistes dans les 4 locales et le repli Rust, et les tests 15/16 qui les exigent (M49, M50) ; (3) M51 et le `.partial` d'un autre écrivain ; (4) les détails de journal par étape et `try_exists` en `Err` ; (5) la ventilation partagée des « gestes » ; (6) le manuel et son PDF aplati (ligne `sudo chown` à 76 caractères, `lstlisting` ASCII). +Aucun code de la story n'est encore écrit. + +## Lentilles + +- **R — Regression hunter** : la dernière remédiation (`git show e590935f`) a-t-elle introduit des défauts ? Contradictions entre AC, tâches, Dev Notes et choix du registre ; frontière avec les fiches sœurs ; faits recopiés sans vérification (numéros de ligne, fonctions, codes, clés i18n) — revérifie-les au code (`grep -nF`) ; recompte des AC et tâches ; implémentable sans deviner ? +- **F — Full-scope adversary** : la conception elle-même. Les chemins de code qui écrivent ou lisent ce que la story + change (inventorier les sites NON résolus, pas énumérer ceux qui marchent) ; transactions, verrous, ordre des + contrôles ; tests existants qui changeront de sens ; frontend qui lit les codes ou champs touchés (`grep -rn` dans + `frontend/src`) ; i18n 4 locales et replis ; le **manuel** (`docs/manual/fr/*.tex` **et le PDF aplati** : + `pdftotext docs/manual/fr/user-manual.pdf - | tr '\n' ' ' | tr -s ' '` vers `target/gate-logs/`) ; + `docs/api-external.md` ; CHANGELOG ; la règle de découpage (§ « Règle de splitting préventif », amendement D5). + +## Ce que tu rends + +Findings numérotés avec sévérité (CRITICAL/HIGH/MEDIUM/LOW), `fichier:ligne` (fiche et code), **preuve** (commande et +sortie, ou code cité relu), correction proposée. Pour tout finding affirmant qu'un code est absent ou présent : la +sortie d'un `grep -nF`. ⛔ **La liste des axes exercés ET non exercés** — un « 0 finding » sans elle ne compte pas. +⛔ **Écris ton rapport complet** dans `target/gate-logs/15-13b-p6-.md` (R ou F ; autorisé), et rends +comme dernier message **seulement** : ce chemin, le bilan par sévérité, et une ligne par finding MEDIUM ou plus. + +## Interdits + +⛔ N'écris aucun fichier du dépôt hors `target/gate-logs/` ; aucune commande qui écrit dans le dépôt ou dans une +base : `scripts/*` (dont `scripts/prepare-release.sh`), `make`, `latexmk`, `git commit`/`add`/`checkout`/`stash`, +`sqlx`, `cargo test`/`nextest`/`build`, `npm run`, `npx`, `gh issue create`/`comment`/`edit`, aucune requête SQL +d'écriture. Autorisés : lecture, `grep`, `sed -n`, `git log`/`show`/`diff`, `gh api` en lecture, `pdftotext` +vers `target/gate-logs/`. diff --git a/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md b/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md index 2380d525..0e14e51e 100644 --- a/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md +++ b/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md @@ -5550,6 +5550,29 @@ l'import (#458–#461). - **Rectificatifs** (les entrées d'origine ne sont pas réécrites) : **C-15-13-9** — « cinq modules » ne vaut plus depuis le découpage (C-15-13-16) : la 15-13a en compte deux (`config`, `main`), la 15-13b quatre (`config`, `routes/admin`, `errors`, catalogues `kesh-i18n`) — R4-10 de la validation P4 de la 15-13b. **C-15-13-14** — « un montage ajouté demain est contrôlé sans retouche » n'était vrai que si l'assertion de montage du test 19 ne fixait pas le nombre d'entrées : elle exige désormais « au moins quatre », dont `/data/backup` (R4-5). **Inventaire de la 15-13a** — la ventilation « 29 + 6 → 36 » de la fiche index ne se refaisait pas ; seuls les totaux sont gardés (R4-7). - **Réversible** : oui. +## C-15-13-27 — 15-13b (validation P5) : le message de #576 nomme toujours les deux pistes — rectifie C-15-13-25 et le motif de C-15-13-9 + +- **Contexte** : R5-1 de la validation P5, **né de la remédiation P4**. C-15-13-25 admettait un texte qui « nomme les deux pistes à égalité, ou aucune ». Or l'écartement du contrôle du dossier au démarrage (C-15-13-9, angle mort de la 15-13b) repose sur un message qui « dit la cause au moment où elle compte » : avec l'option « aucune », le cas que #576 rend nominal (instance hors Docker sans `KESH_ADMIN_BACKUP_DIR`) rendrait un 500 qui ne mentionne plus le dossier, et l'écartement n'aurait plus de fondement — sans qu'aucun test le voie. +- **Retenu** (décision de l'orchestrateur) : le message d'échec antérieur à la sauvegarde nomme **toujours** les deux pistes — le dossier de sauvegarde (inscriptible ?) **et** la base de données (joignable ?) — dans les quatre locales et le repli Rust, par des jetons fixés à la lettre (`dossier de sauvegarde`/`base de données`, `Sicherungsordner`/`Datenbank`, `backup folder`/`database`, `cartella di backup`/`database`). Les tests 15 (repli) et 16 (catalogues) exigent leur présence ; mutations **M49** (`en-CH` sans « database ») et **M50** (repli sans « base de données »). La liste des refus du manuel nomme les mêmes deux pistes (F-P5-7). +- **Rectificatifs** (entrées d'origine non réécrites) : **C-15-13-25** — « ou aucune » est retiré ; « trois des cinq échecs antérieurs » devient « cinq des **sept** » (R5-2 : `:172`, `:236`, `:240`, `:246`, `:259` pour la base ; `:473`, `:487` pour le dossier). **C-15-13-9** — le motif d'écartement du `warn!` au démarrage se lit désormais : « le message **nomme les deux causes possibles** au moment où elles comptent » ; il tient tant que le message nomme le dossier. +- **Écartées** : garder « ou aucune » et rouvrir le contrôle au démarrage (plus coûteux, et un dossier monté tardivement ferait mentir le `warn!`) ; deux variantes dossier/base (déjà écartées par C-15-13-25). +- **Réversible** : oui (quatre catalogues, un repli, deux assertions). + +## C-15-13-28 — 15-13b (validation P5) : le branchement d'`avant_sauvegarde` est gardé par un test lexical + +- **Contexte** : F-P5-1 de la validation P5 (d'origine P1, `07e168e1`). Le test 14 exerce la fonction pure `avant_sauvegarde`, mais rien ne vérifiait qu'elle est appliquée aux appels de `check_schema_compat` (`routes/admin.rs:172`) et de `build_keshbackup` (`:259`) : un `.map_err(avant_sauvegarde)` oublié laissait tous les tests verts et rendait le message faux de #576 (ou « l'export n'a pas pu être généré »). Les deux pannes ne sont pas injectables à bon compte. +- **Retenu** : un garde **lexical**, test 20 `avant_sauvegarde_branchee_aux_appels_de_l_import` (`mod tests` de `routes/admin.rs`) — `include_str!("admin.rs")` tronqué au `#[cfg(test)]`, commentaires écartés, chaque appel des deux fonctions rattaché à sa fonction de premier niveau ; dans `full_import` et `run_backup_and_restore`, l'instruction (jusqu'au `;`) contient `.map_err(avant_sauvegarde)` ; dans `full_export` (`:39`), elle ne le contient pas ; assertion de montage : exactement un appel de `check_schema_compat` et deux de `build_keshbackup`. Forme d'appel prescrite à l'AC 15 b (`….await.map_err(avant_sauvegarde)?`). Mutations **M47** (`:172`) et **M48** (`:259`) **couvertes**. +- **Pourquoi c'est fiable** : la forme est unique et prescrite ; toute écriture que le test ne reconnaît pas (fermeture, conversion différée, alias) le fait rougir **à tort**, jamais passer à tort — un faux rouge coûte une ligne, un faux vert est muet. Le précédent du dépôt est le contrôle (L) de `configuration_transmise.rs`, lexical lui aussi. L'appel de l'export est contrôlé dans l'autre sens, pour que le garde ne pousse pas à convertir partout. Reste angle mort : aucune panne effective ne traverse jusqu'à la réponse HTTP. +- **Écartées** : l'angle mort seul, avec mutation déclarée non couverte (le garde est bon marché et ferme la mutation) ; extraire `sauvegarde_pre_import(pool, dir)` et la tester sur une base dégradée (lourd, change la frontière du verrou, et une table supprimée fait d'abord rougir `check_schema_compat` en 400). +- **Réversible** : oui (un test). + +## C-15-13-29 — 15-13b (validation P5) : précisions de C-15-13-21 — on ne supprime que ce qu'on a créé, `try_exists` en erreur vaut échec + +- **Contexte** : F-P5-3, R5-3/F-P5-8 de la validation P5 (nés de la remédiation P4). C-15-13-21 ne disait pas si l'échec de `create_new` nettoyait le `.partial` (qui n'est alors pas celui de l'appel), ni ce que vaut un `try_exists` en `Err`, ni les détails journalisés des étapes neuves. +- **Retenu** : l'échec de `create_new` ne supprime **rien** (test 12 : le `.partial` préexistant garde son contenu ; mutation **M51**) ; seul le `.partial` créé par l'appel est nettoyé ; `try_exists` en `Err` = **échec** (jamais « absent », qui rouvrirait l'écrasement) ; un détail journalisé par étape, qui nomme le chemin (AC 9 c), non testé à la lettre. +- **Écartées** : nettoyer sur toute erreur (supprimerait le fichier d'un autre écrivain) ; traiter `Err` comme absent. +- **Réversible** : oui. + ## C-15-13a-1 — 15-13a (T0) : le manuel dit le refus de Compose tel qu'il est mesuré — `config`, `pull` et `up` refusent, `ps`, `logs`, `exec`, `stop` restent utilisables, et la variable nommée change d'un lancement à l'autre - **Contexte** : l'AC 11 f affirmait, sous réserve de mesure au T0, que « le refus de Compose frappe toute sous-commande » (`ps`, `logs`, `exec`, `stop`, `down`) et que le script de sauvegarde lancé par `cron` échoue dès que `.env` perd une des deux lignes ; l'AC 11 j, que Compose nomme « la première » variable et que « la seconde apparaît au lancement suivant ». Mesuré le 2026-10-09 (Docker Compose 2.40.3, projet jetable `kesh1513a-t0`, service `mariadb` en marche, `env -u MARIADB_ROOT_PASSWORD -u MARIADB_PASSWORD`) : `config`, `pull`, `up -d` et `up -d kesh-api` sortent en code 1 avec `required variable … is missing a value` ; `ps`, `logs`, `exec -T mariadb true` et `stop` sortent en 0 et font leur travail. Et, sur 12 lancements de la même commande, la variable nommée varie : `MARIADB_ROOT_PASSWORD`, `MARIADB_PASSWORD` ou `DATABASE_URL` (de `kesh-api`, qui nomme `MARIADB_PASSWORD`) — ordre non déterministe. From 9e7a01d8925b14233939a77eee51f138c27f53a6 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Fri, 9 Oct 2026 07:11:31 +0200 Subject: [PATCH 02/12] =?UTF-8?q?docs(15-13b):=20T0=20=E2=80=94=2012=20LOW?= =?UTF-8?q?=20de=20la=20P6=20appliqu=C3=A9s,=20relecture=20contre=20200f5e?= =?UTF-8?q?79,=20mesures=20compose=20config=20et=20manuel=20d'avant=20(ref?= =?UTF-8?q?s=20#552,=20refs=20#576)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- ...13b-sauvegarde-avant-import-persistante.md | 86 +++++++++++++++---- .../epic-15-choix-autonomes.md | 7 ++ .../sprint-status.yaml | 2 +- 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md b/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md index cb2279e3..5b4ed05a 100644 --- a/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md +++ b/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md @@ -32,7 +32,15 @@ l'autre ajoute. Elles touchent les **mêmes fichiers**, à des endroits distinct et les **PDF versionnés** `admin-manual.pdf` et `marketing-brochure.pdf`, que les deux régénèrent. La seconde mergée rebase et **recompte les gestes** du paragraphe « Pour qui garde son fichier compose » (AC 11 f) ; un conflit sur un PDF ne se rebase pas : on fusionne les `.tex`, puis on **régénère** les PDF -(`make fr`) et on rejoue les contrôles aplatis des **deux** fiches (R4-3 de la validation P4 de la 15-13a). **Ordre suggéré : après la 15-13a** (#551 est P2, sécurité ; #552 et #576 P3) ; l'ordre inverse +(`make fr`) et on rejoue les contrôles aplatis des **deux** fiches (R4-3 de la validation P4 de la 15-13a). +**Conflits certains au merge** (F-P6-1 de la validation P6) : `CHANGELOG.md` — le paragraphe d'une seule +ligne « ⚠️ Action requise » (`:46` à la spécification, `:56` au `HEAD` `200f5e79`) porte les cinq phrases +des deux fiches, dont **une même phrase** (« Une variable que `.env` ne pose pas… prend son défaut sans +avertissement — la sauvegarde pré-import reste dans `/tmp` ») réécrite par la 15-13a au début et par la +15-13b à la fin — et `admin-manual.tex`, le paragraphe « Pour qui garde son fichier compose » +(`:1793` à la spécification, `:1796` au `HEAD`). Git fusionne par lignes : la **seconde** mergée résout à la +main, en relisant la phrase « défaut sans avertissement » **entière** et en refaisant le décompte des gestes. +**Ordre suggéré : après la 15-13a** (#551 est P2, sécurité ; #552 et #576 P3) ; l'ordre inverse est possible sans autre effet que le recompte. Release visée : **v0.13.0**, la même que la 15-11a — la section du manuel « Passer à la 0.13.0 » et l'entrée `[0.13.0]` du CHANGELOG sont **complétées**, pas doublées. @@ -87,7 +95,7 @@ laisser lisible par tous (AC 9, C-15-13-4). | `crates/kesh-api/src/routes/admin.rs:236`, `:240`, `:246` ; `admin_backup/import.rs:204` (via `check_schema_compat`, appelé `admin.rs:172`) | échecs **antérieurs** à la sauvegarde, même variante, même message | | `crates/kesh-api/src/admin_backup/export.rs:103-105` (`map_db` → `AdminFullExportFailed`), appelé par `build_keshbackup` (`admin.rs:259`) | échec de lecture de la base pendant la sauvegarde : message « l'**export** n'a pas pu être généré », alors que l'utilisateur importait | -Le frontend affiche le `message` du corps pour un 500 (`AdminRestorePanel.svelte:82-84`) : aucun code client +Le frontend affiche le `message` du corps pour un 500 (`frontend/src/lib/features/admin-restore/AdminRestorePanel.svelte:81-84`) : aucun code client ne dépend de `ADMIN_FULL_IMPORT_FAILED` (`grep -rn ADMIN_FULL_IMPORT frontend/src` → vide). Les échecs **postérieurs** à l'écriture (`admin.rs:283` à `:462`, douze sites) disent vrai : la sauvegarde existe et la transaction est annulée. @@ -166,7 +174,8 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* C-15-13-8.) (a) `write_pre_import_backup` (`routes/admin.rs:471`) garde le choix du nom (inchangé) et délègue l'écriture à une fonction factorisée **sur un chemin donné**, `write_backup_file(path: &Path, bytes: - &[u8]) -> Result<(), AppError>` (privée au module, appelée par les tests 11 et 12). **Écriture par + &[u8]) -> Result<(), AppError>` (privée au module ; appelée directement par le test 12, à travers `write_pre_import_backup` par le + test 11 — F-P6-3 de la validation P6). **Écriture par fichier temporaire, puis renommage** (F-P4-6 de la validation P4, décision de l'orchestrateur, C-15-13-21) : elle crée `.partial` — **même dossier**, donc même système de fichiers — par `tokio::fs::OpenOptions` avec `create_new(true)` et, sous `#[cfg(unix)]`, `mode(0o600)` ; écrit les @@ -192,6 +201,9 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* d'écraser et la variante d'erreur. (b) Le dossier, quand Kesh le crée, l'est en `0o700` (`tokio::fs::DirBuilder`, `recursive(true)`, `mode(0o700)` sous `#[cfg(unix)]`) ; un dossier **existant** (le montage de l'hôte) n'est pas modifié. + Le mode s'applique à **tous les niveaux créés** par l'appel récursif, parents compris (R6-3 = F-P6-6 de + la validation P6) : un `KESH_ADMIN_BACKUP_DIR` sous un parent absent crée ce parent en `0700` aussi — + sans conséquence dans le conteneur (root), écrit au doc-comment et aux angles morts. (c) Une erreur rend `AppError::AdminPreImportBackupFailed` (AC 15) avec un détail **par étape**, qui nomme le chemin (R5-3/F-P5-8 de la validation P5 : le journal est le seul diagnostic, le corps n'expose rien) — « création répertoire backup '…' : … » (inchangé), « création fichier partiel '…' : … » @@ -275,9 +287,13 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* `sudo chown /volume1//kesh-pre-import-<...>.keshbackup` en mesure **76** (`printf '%s' … | wc -m`, remesuré en remédiation P5 avec `<...>`), soit la limite exacte ; si la mise en page l'exige, elle est coupée par `\`. **Le bloc est en ASCII** (F-P5-6 de la validation P5) : `<...>` - et non `<…>` (U+2026) — aucun `lstlisting` du manuel ne porte de caractère hors ASCII, et `listings` - sous XeLaTeX en place mal certains ; la prose, elle, garde `…`. Contrôle aplati positif ajouté à l'AC 11 l - pour la ligne entière. **Ouverture de la section** (`:1643`, « sans accès + et non `<…>` (U+2026) — **par prudence** : le contrôle aplati ne dépend ainsi d'aucun rendu de glyphe + par `listings` (R6-1 de la validation P6 : dix `lstlisting` du manuel portent déjà du non-ASCII ; la + règle vaut pour ce bloc, non pour le manuel) ; la prose, elle, garde `…`. Contrôle aplati positif ajouté à l'AC 11 l + pour la ligne entière — **si la ligne est coupée** à la mise en page (`breaklines`, ou `\`), le contrôle + suit la forme coupée relevée au T5, écrite au Dev Agent Record (F-P6-5 de la validation P6). + **Vocabulaire** (F-P6-8) : aux sites que la story réécrit (`:757`, `:1699` à la spécification), le + manuel dit « dossier de sauvegarde », le mot même du message de l'AC 15. **Ouverture de la section** (`:1643`, « sans accès SSH ni ligne de commande ») : vraie pour l'export et l'import par l'écran, mais la section reçoit désormais un geste en SSH ; une phrase la précise — la sauvegarde de sécurité **pré-import** fait exception, son rapatriement demande un accès SSH (voir plus bas) — (F-P4-7). Le tableau `:1712` @@ -392,7 +408,8 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* `routes/admin.rs`, par une fonction pure `avant_sauvegarde(AppError) -> AppError` qui change `AdminFullImportFailed` et `AdminFullExportFailed` en `AdminPreImportBackupFailed` et rend toute autre variante telle quelle (`ImportSchemaMismatch` reste un 400). `admin_backup` n'est pas modifié. - **Forme prescrite de l'appel** : `….await.map_err(avant_sauvegarde)?`, dans l'instruction même de + **Forme prescrite de l'appel** : `….await.map_err(avant_sauvegarde)?` (rustfmt la répartit sur + plusieurs lignes : sans effet, le test 20 lit l'instruction jusqu'au `;` — R6-2 de la validation P6), dans l'instruction même de l'appel — pas de fermeture `|e| avant_sauvegarde(e)`, pas de conversion différée. **Le branchement est gardé** (F-P5-1 de la validation P5, C-15-13-28) : les deux sources ne sont pas injectables à bon compte (une table supprimée fait d'abord rougir `check_schema_compat` en 400), si bien que le test 14 @@ -405,8 +422,8 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* journaux du serveur, puis réessayer — **sans orienter vers une seule cause** (F-P4-5 de la validation P4, C-15-13-25) : sur les **sept** sites antérieurs à l'écriture (AC 15 a-b, R5-2 de la validation P5), **cinq** relèvent de la **base** (`:172` lecture du schéma, `:236` transaction, `:240` verrou, `:246` - ligne `_kesh_version` absente, `:259` lecture des données — et erreurs `zip`/`serde_json` de - `build_keshbackup`), **deux** du **dossier** (`:473`, `:487` devenu `write_backup_file`, avec ses étapes + ligne `_kesh_version` absente, `:259` export de la base — lecture des données ou assemblage du ZIP + de `build_keshbackup`, aucune erreur `serde_json` n'y naît, R6-4 de la validation P6), **deux** du **dossier** (`:473`, `:487` devenu `write_backup_file`, avec ses étapes `.partial`). **Le texte nomme toujours les deux pistes, à égalité** (R5-1 de la validation P5, C-15-13-27, qui rectifie C-15-13-25 : l'option « n'en nommer aucune » est **retirée**) — le dossier de sauvegarde (inscriptible ?) **et** la base de données (joignable ?) —, dans les quatre locales **et** le @@ -432,7 +449,7 @@ des AC 10 à 14 qui portent sur MariaDB aussi.)* même que le repli Rust contient « aucune sauvegarde n'a été créée ». **Les deux pistes sont exigées de même** (R5-1) : le test 16 vérifie que chaque texte **contient** ses deux jetons, le test 15 que le repli Rust contient « dossier de sauvegarde » et « base de données » (mutations **M49**, **M50**). - (d) Frontend **non touché** : un 500 affiche le `message` du corps (`AdminRestorePanel.svelte:82-84`), + (d) Frontend **non touché** : un 500 affiche le `message` du corps (`frontend/src/lib/features/admin-restore/AdminRestorePanel.svelte:81-84`), aucun code client ne lit `ADMIN_FULL_IMPORT_FAILED`. Le test de parité des catalogues (`kesh-i18n/src/loader.rs:729-800`) reste vert sans entrée neuve dans `dette-parite-connue.txt`. (e) Manuel : la liste des refus de `sec:backup-ui-keshbackup` (AC 11 e). @@ -571,15 +588,15 @@ Numérotation de la fiche unique conservée. | 14 | `avant_sauvegarde_convertit_les_echecs_anterieurs` (nouveau, unitaire, `routes/admin.rs`) — `AdminFullImportFailed` et `AdminFullExportFailed` → `AdminPreImportBackupFailed`, détail conservé ; `ImportSchemaMismatch` et `InvalidBackupStructure` rendus tels quels | AC 15 b | **M30** `avant_sauvegarde` rend l'erreur telle quelle · **M31** `avant_sauvegarde` convertit toute variante (un 400 devient un 500) | | 15 | `pre_import_backup_failed_maps_to_500_without_promise` (nouveau, `errors.rs`, repli Rust — aucun catalogue n'est chargé dans les tests de la bibliothèque, `init_error_i18n` n'y est jamais appelé) — 500, `ADMIN_PRE_IMPORT_BACKUP_FAILED`, message sans le détail et sans promesse de sauvegarde, et qui **contient** « aucune sauvegarde n'a été créée » (F-P4-4) ainsi que les deux pistes, « dossier de sauvegarde » **et** « base de données » (R5-1 de la validation P5) ; **témoin** : `AdminFullImportFailed` rend `ADMIN_FULL_IMPORT_FAILED` et un repli **différent** | AC 15 a, c | **M32** le bras de la variante neuve rend le code et la clé d'`AdminFullImportFailed` · **M50** repli Rust de la variante neuve sans « base de données » (une seule piste) | | 16 | `catalogues_distinguent_l_echec_de_sauvegarde` (nouveau, `errors.rs`, `I18nBundle::load` sur `concat!(env!("CARGO_MANIFEST_DIR"), "/../kesh-i18n/locales")` — chemin indépendant du répertoire courant (F-P3-10) —, **sans** toucher au catalogue global) — pour chacune des quatre locales : `error-admin-pre-import-backup-failed` ≠ `error-admin-full-import-failed` de la même locale ; hors `fr-CH`, ≠ le texte `fr-CH` (sinon `format` est retombé sur le français, `loader.rs:116-121`) ; ne contient pas la **promesse** de l'ancien texte, phrase intérieure de sa parenthèse **sans les parenthèses** (R3-9) : `un backup automatique a été créé avant l'opération`, `vor dem Vorgang wurde automatisch ein Backup erstellt`, `an automatic backup was created before the operation`, `prima dell'operazione è stato creato un backup automatico` — **pas** des sous-chaînes que la négation prescrite contient (`a été créé` est préfixe de « n'a été créée ») ; **assertion de montage** : chaque interdit figure dans `error-admin-full-import-failed` de **sa** locale (texte inchangé), faute de quoi l'interdit est une coquille et le test passe à vide. Vérifié en remédiation P3 : `grep -cF` de chaque interdit (sans parenthèses) → 1 dans le `messages.ftl` de sa locale (`fr-CH:1478`, `de-CH`/`en-CH`/`it-CH:1407`), et dans aucun autre ; `printf '%s' "" \| grep -cF -- ""` → 0 pour les vingt couples (quatre formulations de l'AC 15 c et « Non è stato creato un backup automatico », contre les quatre interdits) ; **et** (F-P4-4 de la validation P4) chaque texte neuf **contient** la négation de sa locale, à la lettre (`aucune sauvegarde n'a été créée`, `Es wurde keine Sicherung erstellt`, `No backup was created`, `Non è stato creato alcun backup`) ; **et** (R5-1 de la validation P5) chaque texte neuf **contient** ses deux jetons de piste, à la lettre (`dossier de sauvegarde`/`base de données`, `Sicherungsordner`/`Datenbank`, `backup folder`/`database`, `cartella di backup`/`database`) | AC 15 c | **M33** texte `de-CH` remplacé par celui d'`error-admin-full-import-failed` · **M34** clé retirée d'`it-CH` (rouge aussi à `parity_between_locales`) · **M38** texte `fr-CH` neuf qui recopie la parenthèse de l'ancien · **M45** texte `fr-CH` neuf réduit à « Échec de l'import. » (sans négation ni promesse) · **M49** texte `en-CH` neuf sans « database » (une seule piste, le dossier) | -| 17 | `full_import_refuses_when_backup_cannot_be_written` (nouveau, `admin_full_import_e2e.rs`) — `admin_backup_dir` = chemin d'un **fichier** existant (la création du dossier échoue même sous root : en CI comme en local) ; `spawn_app` factorisé en `spawn_app_with(pool, config)` (DRY) ; 500, `ADMIN_PRE_IMPORT_BACKUP_FAILED`, message = repli (ce binaire n'appelle pas `init_error_i18n`) ; installation intacte (société et administrateur d'origine, comme le test 18) | AC 15 a, 9 c | **M35** `admin.rs:473` (création du dossier) rend `AdminFullImportFailed` | -| 18 | `full_import_rolls_back_on_insert_failure` (existant, `admin_full_import_e2e.rs:559-605`, complété) — échec **après** la sauvegarde : code `ADMIN_FULL_IMPORT_FAILED` | AC 15 a | **M37** `admin.rs:291` (`restore :`) rend `AdminPreImportBackupFailed` | +| 17 | `full_import_refuses_when_backup_cannot_be_written` (nouveau, `admin_full_import_e2e.rs`) — `admin_backup_dir` = chemin d'un **fichier** existant (la création du dossier échoue même sous root : en CI comme en local) ; `spawn_app` factorisé en `spawn_app_with(pool, config)` (DRY) ; 500, `ADMIN_PRE_IMPORT_BACKUP_FAILED`, message = repli (ce binaire n'appelle pas `init_error_i18n`) ; installation intacte (société et administrateur d'origine, comme le test 18) | AC 15 a, 9 c | **M35** la création du dossier dans `write_pre_import_backup` (`admin.rs:473` à la spécification, réécrite en `DirBuilder` : F-P6-7) rend `AdminFullImportFailed` | +| 18 | `full_import_rolls_back_on_insert_failure` (existant, `admin_full_import_e2e.rs:559-605`, complété) — échec **après** la sauvegarde : code `ADMIN_FULL_IMPORT_FAILED` | AC 15 a | **M37** le site `restore :` de `run_backup_and_restore` (`admin.rs:291`) rend `AdminPreImportBackupFailed` | | 19 | `montages_hors_du_depot` (nouveau, `configuration_transmise.rs`) — pour **chaque** entrée de `MONTAGES`, le dossier de sa source `docker-compose.prod.yml` (`./x` → `x`) a sa ligne dans `.gitignore` sous la forme **ancrée** `/x/` — **seule tolérance** : `log`, pour qui `log/` (ligne existante, `.gitignore:34`) est admise, la liste des tolérances étant une constante qui ne compte que `log` (assertion) — et dans `.dockerignore` (`x/`) ; la liste est **dérivée** de `MONTAGES`, pas recopiée (un cinquième montage ajouté demain est contrôlé sans retouche) ; **assertion de montage** : `MONTAGES` compte **au moins** quatre entrées, dont celle de cible `/data/backup` (R4-5 de la validation P4, C-15-13-26 : un compte exact obligerait à retoucher le test au cinquième montage, contre la phrase précédente), et chaque ligne est cherchée **entière** (après `trim`), pas en sous-chaîne (`log/` ne doit pas satisfaire `/backup/`, ni `backup/` satisfaire `/backup/`) | AC 8 e | **M39** retirer `/backup/` de `.gitignore` · **M40** retirer `backup/` de `.dockerignore` · **M41** `/backup/` remplacé par `backup/` (non ancré) dans `.gitignore` (R3-1) | -| 20 | `avant_sauvegarde_branchee_aux_appels_de_l_import` (nouveau, `mod tests` de `routes/admin.rs`, F-P5-1 de la validation P5, C-15-13-28) — **garde lexicale** : lit `include_str!("admin.rs")` tronqué à la première ligne `#[cfg(test)]` (le module de test ne se lit pas lui-même), lignes dont le `trim_start` commence par `//` écartées (les doc-comments nomment `build_keshbackup` : `:129`, `:256`) ; repère chaque **appel** `check_schema_compat(` et `build_keshbackup(` (identifiant suivi de `(`, précédé ni de `fn ` ni d'un `use`), le rattache à la fonction de premier niveau qui l'entoure (dernière ligne `pub async fn ` / `async fn ` en colonne 0 qui le précède) et lit son **instruction** (texte jusqu'au `;` suivant, retours à la ligne compris : `rustfmt` peut la couper) — dans `full_import` et `run_backup_and_restore`, l'instruction **contient** `.map_err(avant_sauvegarde)` ; dans `full_export`, elle **ne le contient pas** ; **assertion de montage** : exactement **un** appel de `check_schema_compat` et **deux** de `build_keshbackup` (un par route), sinon échec qui nomme le décompte trouvé — un garde qui ne trouve rien passerait à vide, et un appel ajouté demain doit être trié | AC 15 b | **M47** `.map_err(avant_sauvegarde)` retiré à l'appel de `check_schema_compat` (`:172`) · **M48** retiré à l'appel de `build_keshbackup` (`:259`) | +| 20 | `avant_sauvegarde_branchee_aux_appels_de_l_import` (nouveau, `mod tests` de `routes/admin.rs`, F-P5-1 de la validation P5, C-15-13-28) — **garde lexicale** : lit `include_str!("admin.rs")` tronqué à la première ligne `#[cfg(test)]` (le module de test ne se lit pas lui-même), lignes dont le `trim_start` commence par `//` écartées (les doc-comments nomment `build_keshbackup` : `:129`, `:256`) ; repère chaque **appel** `check_schema_compat(` et `build_keshbackup(` (identifiant suivi de `(`, précédé ni de `fn ` ni d'un `use`), le rattache à la fonction de premier niveau qui l'entoure (dernière ligne `pub async fn ` / `async fn ` en colonne 0 qui le précède) et lit son **instruction** (texte jusqu'au `;` suivant, retours à la ligne compris : `rustfmt` peut la couper) — dans `full_import` et `run_backup_and_restore`, l'instruction **contient** `.map_err(avant_sauvegarde)` ; dans `full_export`, elle **ne le contient pas** ; **assertion de montage** : exactement **un** appel de `check_schema_compat` et **deux** de `build_keshbackup` (un par route), sinon échec qui nomme le décompte trouvé — un garde qui ne trouve rien passerait à vide, et un appel ajouté demain doit être trié | AC 15 b | **M47** `.map_err(avant_sauvegarde)` retiré à l'appel de `check_schema_compat` (`:172`) · **M48** retiré à l'appel de `build_keshbackup` (`:259`) · **M52** `.map_err(avant_sauvegarde)` **ajouté** à l'appel de `build_keshbackup` de `full_export` (`:39`) — branche négative (F-P6-2 de la validation P6) | **Décompte** (recompté sur le tableau) : 12 lignes, toutes côté Rust, qui portent **14 fonctions de test** (10 neuves : lignes 6 b, 11, 12 ×2, 14, 15, 16, 17, 19, 20 ; 4 existantes complétées : lignes 3, 5, 6 a, 18), dans cinq fichiers existants (`configuration_transmise.rs`, `config.rs`, `routes/admin.rs`, `errors.rs`, -`admin_full_import_e2e.rs`) ; **27 mutations** (M9–M11, M14, M15, M24–M26, M30–M41, M45–M51), toutes +`admin_full_import_e2e.rs`) ; **28 mutations** (M9–M11, M14, M15, M24–M26, M30–M41, M45–M52 — M52 ajoutée au T0, F-P6-2), toutes attendues rouges. M11 rougit **deux** familles ((T) et (F)), M15 et M34 deux tests chacune : attendu, le relever. **M26** change de sens avec l'écriture par `.partial` : `create_new` porte désormais sur le `.partial`, et c'est la vérification d'existence du nom final qui empêche l'écrasement. Non couverts par @@ -702,7 +719,7 @@ validation P3. **Résolu** = un AC le traite ; **assumé** = angle mort écrit. | `DOCKER_START.md` § *Notes* (`:140`, puce neuve — le fichier n'a pas de section « dossiers ») | `./backup` | résolu (AC 12 a', F-P5-4) | | `admin-manual.tex:757`, `:807-822` (dont `:812`, énumération des montages fixes) | `sec:env-vars` (ligne `KESH_ADMIN_BACKUP_DIR`, montages) | résolu (AC 11 d, F-P4-3) | | `admin-manual.tex:1643` (« sans accès SSH ni ligne de commande », ouverture de `sec:backup-ui-keshbackup`) | contradiction avec le rapatriement en SSH de la même section | résolu (AC 11 e, F-P4-7 : phrase d'exception) | -| `admin-manual.tex:1712` (« restauration self-service sans SSH »), `website/roadmap.html:231` (« SSH-free migration and restore ») | promesse de l'export/import par l'écran | **assumé** — vraie pour un `.keshbackup` exporté ; la sauvegarde pré-import en fait exception, dite à `:1643` (F-P4-7) | +| `admin-manual.tex:1712` (« restauration self-service sans SSH »), `website/roadmap.html:231` (« SSH-free migration and restore »), `README.md:48` (« sans accès SSH ✓ », F-P6-4 de la validation P6) | promesse de l'export/import par l'écran | **assumé** — vraie pour un `.keshbackup` exporté ; la sauvegarde pré-import en fait exception, dite à `:1643` (F-P4-7) | | `admin-manual.tex:1417-1425`, `:1664`, `:1667-1672`, `:1699` | sauvegarde | résolu (AC 11 e, i) | | `admin-manual.tex:1452-1477`, `:2312-2317` (`BACKUP_DIR` du script de sauvegarde) | homonyme : variable de shell, pas de Kesh | **assumé** — hors sujet (le `cd` et `exec db` du même script : 15-13a, AC 11 h) | | `admin-manual.tex:1757-1841` — paragraphe sauvegarde, `:1773` (« Effets sans refus »), `:1793` (« deux gestes », « Sous `environment:` ») | mise à jour 0.13.0 — parties sauvegarde | résolu (AC 11 f) | @@ -752,6 +769,9 @@ existante. peut pas dire laquelle : le journal le dit), les tests 15 et 16 l'exigent (M49, M50), et les recettes hors Docker posent la variable (AC 16). Ce fondement tient **tant que** le message nomme le dossier : s'il cessait de le faire, l'écartement serait à rouvrir. +- **Parents créés en `0700`** (R6-3 = F-P6-6) : `DirBuilder` récursif pose `0700` sur chaque niveau qu'il + crée ; un `KESH_ADMIN_BACKUP_DIR` sous un parent absent (hors Docker, sous root : `/data`) le crée fermé. + Sans conséquence dans le conteneur (root) ; écrit au doc-comment, non traité. - **Montage d'hôte `./backup` créé `root:root` `0755`** : l'exploitant qui le veut fermé fait `chmod 700 backup` (manuel) ; les fichiers sont `0600` de toute façon. @@ -932,3 +952,39 @@ converties à l'appel (AC 15 b), précisément pour ne pas ouvrir un module de p l'écrire), R6-4 (pas de `serde_json` dans `export.rs`), F-P6-1 (conflit certain avec la 15-13a sur `CHANGELOG.md:46` et `admin-manual.tex:1793` : à écrire aux Dépendances), F-P6-2 (M52 : branche négative du test 20), F-P6-3, F-P6-4 (`README.md:48`), F-P6-5, F-P6-7, F-P6-8 (« dossier » contre « répertoire »). + +- **2026-10-09 — T0 du développement** (agent de développement, Opus 5.5, en autonomie ; worktree `kesh-15-13b`, + base `200f5e79` + `dd9c3b1f`). + - **Les 12 LOW de la P6 appliqués** : R6-1 (AC 11 e, motif ASCII « par prudence »), R6-2 (AC 15 b, rustfmt), + R6-3 = F-P6-6 (AC 9 b et angle mort « parents créés en `0700` »), R6-4 (AC 15 c, ZIP et non `serde_json`), + F-P6-1 (§ *Dépendances* : conflits certains sur le paragraphe « Action requise » du CHANGELOG et sur + « Pour qui garde son fichier compose »), F-P6-2 (**M52**, branche négative du test 20 ; **28 mutations**, + union avec la 15-13a : 52, M52 absente de la 15-13a — vérifié), F-P6-3 (AC 9 a), F-P6-4 (`README.md:48` à + l'inventaire, ligne assumée — **42 lignes d'inventaire inchangées**, ligne enrichie), F-P6-5 (AC 11 e : + forme coupée), F-P6-7 (M35, M37 désignées par le texte ; chemin et lignes `:81-84` d'`AdminRestorePanel`), + F-P6-8 (AC 11 e : « dossier de sauvegarde » au manuel). Angles morts : **11**. + - **Relecture contre `200f5e79`** (la 15-7a2, la 15-12a et la 15-6a sont mergées depuis la spécification) : + **aucun écart de fond**, seulement des numéros de ligne décalés. `routes/admin.rs`, `config.rs`, + `configuration_transmise.rs`, `admin_full_import_e2e.rs`, les compose, `.env.example`, `docs/testing.md`, + `README.md`, `DOCKER_START.md`, `.gitignore`, `.dockerignore`, `CLAUDE.md` : **inchangés** (`git diff --stat + de285ea8 200f5e79` vide sur eux) — tous les numéros de la fiche y valent. Décalés : `errors.rs` +13 + (`AdminFullExportFailed` `:459`, `AdminFullImportFailed` `:467`, doc `:461-466`, bras `:1874-1884`) ; + catalogues +5 (`error-admin-full-import-failed` `fr-CH:1483`, `de/en/it-CH:1412`) ; `admin-manual.tex` +3 + (`:1646` ouverture de la section, `:1796` « Pour qui garde… ») ; `CHANGELOG.md` +10 (`:54` entrée #550 + « Inchangé », `:56` paragraphe « Action requise »). Les AC citent la ligne de la spécification ; le + développement se repère au texte. + - **Mesures** (`target/gate-logs/15-13b-t0-*`) : (1) **graphie** de `docker compose config` (client seul, + sans démon) pour `- ./backup:/data/backup` : forme longue `type: bind` / `source: …/backup` / + `target: /data/backup` / `bind: create_host_path: true`, **identique** pour les deux compose ; + `grep -c 'target: /data/backup'` → **1** avec le montage ; sur les compose **sans** montage avec + `KESH_ADMIN_BACKUP_DIR=/data/backup` dans `.env` → **0** (alors que `grep -c '/data/backup'` → 1, la ligne + d'environnement) : le contrôle de l'AC 11 f est discriminant, motif inchangé. (2) **`make fr` d'avant** : + `.log` de référence (55 lignes `Overfull`), PDF aplatis ; contrôles de l'AC 11 l sur le PDF d'avant : + positifs tous à **0** (`/data/backup`, `sudo cp backup/`, `kesh-pre-import`, `keshbackup.partial`, + `target: /data/backup`, ligne `sudo chown` entière, `base de données.{0,40}inaccessible`) ; négatifs + présents (`défaut /tmp` ×2, `deux gestes` ×1, `BACKUP_DIR.{0,90}/tmp` ×3) — tous discriminants. PDF + versionnés restaurés (`git checkout`). (3) **Rapatriement sur conteneur jetable** : **non mesuré au T0** — + le démon Docker de la station est bloqué au noyau (tâches `dockerd` en état D sur un rw-semaphore depuis + 07:06, `docker run` expire à 60 s), et le relancer toucherait `kesh-mariadb-dev` (interdit). Reporté au + T10, choix C-15-13b-1. + - **Registre** : `15-13` (`split`) et `15-13b` (`in-progress`) ajoutées à `sprint-status.yaml`. diff --git a/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md b/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md index 0e14e51e..2708bb7f 100644 --- a/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md +++ b/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md @@ -5621,3 +5621,10 @@ l'import (#458–#461). - **Retenu** : dette écrite à la fiche (« Ce que la story ne fait pas »), rattachée à **#538** — l'atomicité des quatre premières validations fait disparaître l'état non relançable, et le message redevient juste. Atteinte pratiquement impossible avec le plan PME embarqué. B-2 (boucle `InactiveOrInvalidAccounts`) reste à l'arbitrage de Guy (C-15-7-14). - **Écarté** : changer le texte maintenant (code de production en remédiation de fin de boucle, qui rouvrirait la boucle de revue pour un LOW). - **Réversible** : oui (une chaîne). + +## C-15-13b-1 — 15-13b (T0) : le rapatriement n'est pas rejoué au T0, le démon Docker étant bloqué + +- **Contexte** : le T0 prescrit de rejouer, sur un conteneur jetable, l'écriture d'un fichier `0600` par un conteneur root dans un dossier monté, puis le rapatriement `sudo cp` + `sudo chown`. Le 2026-10-09 vers 07:10, toute création de conteneur expire (`docker run` → 124 après 60 s) : le noyau signale des tâches `dockerd` en état D, bloquées sur un rw-semaphore (`journalctl`, « blocked for more than 122 seconds »). Le démon sert encore `ps`/`info`. Les créations interrompues laissent des noms réservés sans conteneur. `sudo` n'est pas utilisable sans mot de passe depuis l'agent. +- **Retenu** : faire au T0 ce qui ne dépend pas du démon (`docker compose config`, client seul : graphie et contrôle rouge mesurés ; `make fr`), écrire le manuel selon la fiche, et **retenter** le rapatriement au T10 ; s'il est toujours impossible, l'écrire au Dev Agent Record comme **non mesuré**, à rejouer par l'orchestrateur ou en recette. `sudo` y est joué par un conteneur root (même effet de droits), ce qui sera dit. +- **Écartées** : redémarrer `dockerd` (emporterait `kesh-mariadb-dev` et les bases des autres agents — interdit) ; attendre sans fin ; déclarer le geste vérifié par raisonnement (« une hypothèse éliminée par raisonnement n'est pas une hypothèse testée »). +- **Réversible** : oui — la mesure se rejoue à tout moment. diff --git a/_bmad-output/implementation-artifacts/sprint-status.yaml b/_bmad-output/implementation-artifacts/sprint-status.yaml index 3d17fd2d..fc5ab379 100644 --- a/_bmad-output/implementation-artifacts/sprint-status.yaml +++ b/_bmad-output/implementation-artifacts/sprint-status.yaml @@ -379,7 +379,7 @@ development_status: 15-12b-filet-sous-un-bilan-clos: done # 2026-10-09 — closes #543 (P1). DONE : revue de code close (P1 Sonnet ×3 : 0 au-dessus de LOW, 16 LOW traités sans ligne de production ; B-1 = E-1 suivi par #569 ; C-15-12b-4) ; rebasée sur 200f5e79 (15-7a2) ; gates sur l'état rebasé : backend 2993/2993, Vitest 1116/1116, E2E 246 / 7 KF-029 + 2 KF-045 + 1 pollution rejouée verte. DÉVELOPPÉE (worktree kesh-15-12b) : gates backend 2970/2970, Vitest 1115/1115, E2E 245 / 7 KF-029 + 2 KF-045 ; mutations (v) (vi) (vii-a) (vii-b) tuées ; choix C-15-12b-1 à 3. Historique : Créée au découpage de la 15-12 (C107), EN VALIDATION. ⛔ DÉPEND de 15-12a. Filet LATER_FISCAL_YEAR_CLOSED aux deux points de passage du journal (create_in_tx_inner sans verrou, delete_in_tx sans condition — la dévalidation), inventaire fermé des écrivains, lot de rapprochement aux trois voies dont accept_one_rule (C108), bandeau d'état hérité et réparation LIFO, autres écrans, prédicteurs d'annulation en angle mort (C120). 11 AC (numéros d'origine), 9 tâches ; 10 modules (dérogation écrite, C121). Avant la 15-1a de préférence (delete_in_tx partagé, C112 ; précédence C117). P3 (Opus ×2) remédiée le 2026-10-09. Prochaine : P4 ciblée (une lentille) sur le diff de la remédiation P3. 15-13-mariadb-et-sauvegarde: split # 2026-10-09 DÉCOUPÉE après la validation P3 (signal D5 de recyclage levé deux passes de suite, C-15-13-16) en 15-13a (#551, MariaDB) + 15-13b (#552, #576, sauvegarde). Fiche devenue index ; fiche unique au commit 8a9bcd27. 15-13a-mariadb-non-publiee-mots-de-passe-obligatoires: done # 2026-10-09 — closes #551 (P2, sécurité), refs #577, refs #578. DONE : revue de code close (P1 Sonnet ×3 1 MEDIUM distinct, E-1 = A-1 → remédiation 7faa16bb sans code Rust de production → P2 ciblée Haiku 0) ; base 200f5e79 inchangée (rebase sans objet) ; gates sur 7e471fd4 : backend 2980/2980, Vitest 1095/1095, E2E 245 / 7 KF-029 + 2 KF-045 du matin, étape CI « Validate compose files » rejouée verte. (worktree kesh-15-13a, branche story/15-13a-mariadb-non-publiee). Développement : 24 mutations rouges ; recette MariaDB rejouée sur conteneurs jetables (T7). Écarts T0 : refus de Compose limité à config/pull/up, variable nommée non déterministe (C-15-13a-1) ; étape CI renforcée, M27 survivait (C-15-13a-2) ; recette ALTER USER refondue (C-15-13a-3), volume laissé à #575 (C-15-13a-4). Validation close (P1 8 MEDIUM → P2 6 → P3 5 → découpage → P4 1 → P5 ciblée 0). - 15-13b-sauvegarde-avant-import-persistante: ready-for-dev # 2026-10-09 — #552, #576. Créée au découpage de la 15-13 (C-15-13-16) ; indépendante de la 15-13a en code, mêmes fichiers (rebase et recompte des gestes du manuel). + 15-13b-sauvegarde-avant-import-persistante: in-progress # 2026-10-09 — closes #552 #576, refs #558. Validation close à la P6 (trend 8→6→5→découpage→4→2→0 MEDIUM). Développement en cours (worktree kesh-15-13b). epic-15-retrospective: optional # --- v0.2 : Epics ajoutés au scoping 2026-06-04 (cf. _bmad-output/planning-artifacts/epic-17.md) --- # Ordre d'exécution v0.2 : E17 (infra) DÉMARRE, puis E11 TVA, E16 Facturation, E12, E13, E14, E15. From fdb4c0782de9c54ebef332e3a34c2b35f5d8065c Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Fri, 9 Oct 2026 07:12:05 +0200 Subject: [PATCH 03/12] =?UTF-8?q?feat(15-13b):=20T1=20=E2=80=94=20les=20co?= =?UTF-8?q?mpose=20montent=20./backup=20sur=20/data/backup=20;=20dossiers?= =?UTF-8?q?=20mont=C3=A9s=20ignor=C3=A9s=20de=20git=20et=20du=20contexte?= =?UTF-8?q?=20de=20build=20(refs=20#552)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .dockerignore | 6 ++++++ .env.example | 18 +++++++++++++----- .gitignore | 8 ++++++++ docker-compose.prod.yml | 6 ++++++ docker-compose.yml | 6 ++++++ 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/.dockerignore b/.dockerignore index 425f6f96..dce02b4e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -12,3 +12,9 @@ design-artifacts/ docs/ charts/ .claude/ +# Sources des montages par défaut des compose (15-13b, AC 8 e) — hors du +# contexte de build (la sauvegarde pré-import est un secret). +log/ +inbox/ +documents/ +backup/ diff --git a/.env.example b/.env.example index c2de54f6..ed8ffa5a 100644 --- a/.env.example +++ b/.env.example @@ -83,6 +83,9 @@ KESH_PORT=80 # # KESH_PORT=3000 # Alternatives : (i) lancer `cargo run` avec `sudo` (déconseillé), (ii) # ajouter la capability via `sudo setcap 'cap_net_bind_service=+ep' target/debug/kesh-api`. +# Hors Docker, poser aussi KESH_ADMIN_BACKUP_DIR sur un dossier inscriptible +# par l'utilisateur (son défaut /data/backup ne l'est pas) : sans quoi tout +# import d'installation est refusé, avant toute suppression. # --- Compte admin initial (Story v011-5 — double-usage) --- # @@ -179,9 +182,12 @@ KESH_BANK_IMPORT_MAX_MB=10 #KESH_ADMIN_EXPORT_INMEM_MB=50 # Taille max d'un upload .keshbackup à l'import (MiB). Borne [1, 10240], défaut 512. #KESH_ADMIN_IMPORT_MAX_MB=512 -# Répertoire du backup automatique pré-import (filet avant restore destructeur). -# Défaut /tmp (absente ou vide) — PERDU au redémarrage du conteneur ; monter un -# volume persistant. +# Dossier de sauvegarde de sécurité prise avant chaque import d'installation +# (filet avant le restore destructeur ; fichiers SECRETS, en 0600). +# Défaut /data/backup (absente ou vide), monté sur ./backup par les deux compose +# fournis. Une autre valeur doit être un chemin MONTÉ, sans quoi la sauvegarde +# retourne dans le système de fichiers éphémère du conteneur. Hors Docker : un +# dossier inscriptible par l'utilisateur qui lance Kesh. #KESH_ADMIN_BACKUP_DIR=/data/backup # --- Chemins internes (hors Docker seulement) --- @@ -209,7 +215,8 @@ KESH_BANK_IMPORT_MAX_MB=10 # --- Chemins HOST des bind mounts (docker-compose.yml) --- # Sans effet avec `docker-compose.prod.yml` (installation Synology du manuel) : # ses montages sont fixes — `./inbox`, `./documents`, `./log` dans le répertoire -# du compose (issue #558). +# du compose (issue #558), et `./backup` (sauvegarde pré-import, sans variable +# d'hôte). # Avec `docker-compose.yml` : pour rendre l'inbox accessible depuis votre PC # (SMB / Synology Drive), montez ces dossiers sur un dossier PARTAGÉ du NAS — # créez un dossier partagé (ex. « kesh ») avec sous-dossiers inbox/ et @@ -242,7 +249,8 @@ RUST_LOG=info # Chemin HOST du répertoire de logs (bind mount de docker-compose.yml). Pointe # vers un dossier partagé du NAS pour consulter les logs depuis le PC (SMB/Drive). # Sans effet avec `docker-compose.prod.yml` : ses montages sont fixes — `./inbox`, -# `./documents`, `./log` dans le répertoire du compose (issue #558). +# `./documents`, `./log` dans le répertoire du compose (issue #558), et `./backup` +# (sauvegarde pré-import, sans variable d'hôte). #KESH_LOG_HOST_DIR=/volume1/kesh/log # (défaut sans .env : ./log dans le dossier du projet) # diff --git a/.gitignore b/.gitignore index 892fcb9b..96fa4a74 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,14 @@ Thumbs.db *.log # Story v011-1 : répertoire de logs fichier monté par docker-compose.prod.yml log/ +# Sources des montages par défaut des compose (15-13b, AC 8 e) : justificatifs, +# factures déposées et sauvegarde pré-import (SECRET) ne se versionnent jamais. +# Motifs ANCRÉS à la racine : `backup/` seul ignorerait aussi la route versionnée +# frontend/src/routes/(app)/admin/backup/. (`log/` reste non ancré : tolérance +# nommée, aucun dossier log versionné — test montages_hors_du_depot.) +/inbox/ +/documents/ +/backup/ # LaTeX build artifacts (manuels docs/manual/) — ne versionner que .tex + .pdf # (aligne admin-manual sur la convention marketing-brochure ; Story 17-3f) diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 25ada71a..ad9ee265 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -166,6 +166,12 @@ services: # justificatifs archivés vivent dans ./documents. - ./inbox:/data/inbox - ./documents:/data/documents + # Sauvegarde de sécurité prise avant chaque import d'installation (#552) — + # défaut du code /data/backup, monté ici sur ./backup (montage fixe, sans + # variable d'hôte). SECRET (condensés des mots de passe, jetons de session) : + # fichiers en 0600. Dans le périmètre de sauvegarde du dossier du compose + # (Hyper Backup) ; à purger par l'exploitant (une sauvegarde par import). + - ./backup:/data/backup networks: - frontend healthcheck: diff --git a/docker-compose.yml b/docker-compose.yml index 9039b66e..9cd00db7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -151,6 +151,12 @@ services: # Défauts (dev, sans .env) : sous-dossiers ./inbox et ./documents du projet. - ${KESH_INBOX_HOST_DIR:-./inbox}:/data/inbox - ${KESH_DOCUMENTS_HOST_DIR:-./documents}:/data/documents + # Sauvegarde de sécurité prise avant chaque import d'installation (#552) — + # défaut du code /data/backup, monté ici sur ./backup (montage fixe, sans + # variable d'hôte). SECRET (condensés des mots de passe, jetons de session) : + # fichiers en 0600. Dans le périmètre de sauvegarde du dossier du compose + # (Hyper Backup) ; à purger par l'exploitant (une sauvegarde par import). + - ./backup:/data/backup networks: - kesh-network healthcheck: From 050c8b51a72b73d37ffc407d4b064bace1acb0a4 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Fri, 9 Oct 2026 07:46:05 +0200 Subject: [PATCH 04/12] =?UTF-8?q?feat(15-13b):=20T2-T3=20=E2=80=94=20d?= =?UTF-8?q?=C3=A9faut=20/data/backup,=20sauvegarde=20en=200600=20par=20.pa?= =?UTF-8?q?rtial=20sans=20=C3=A9crasement,=20AdminPreImportBackupFailed=20?= =?UTF-8?q?et=20son=20message=20dans=20les=20quatre=20locales=20;=20tests?= =?UTF-8?q?=203,=205,=206,=2011,=2012,=2014=20=C3=A0=2020=20(refs=20#552,?= =?UTF-8?q?=20refs=20#576)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- crates/kesh-api/src/config.rs | 71 +++- crates/kesh-api/src/errors.rs | 142 ++++++- crates/kesh-api/src/routes/admin.rs | 365 ++++++++++++++++-- .../kesh-api/tests/admin_full_import_e2e.rs | 51 ++- .../kesh-api/tests/configuration_transmise.rs | 140 ++++++- crates/kesh-i18n/locales/de-CH/messages.ftl | 1 + crates/kesh-i18n/locales/en-CH/messages.ftl | 1 + crates/kesh-i18n/locales/fr-CH/messages.ftl | 1 + crates/kesh-i18n/locales/it-CH/messages.ftl | 1 + 9 files changed, 721 insertions(+), 52 deletions(-) diff --git a/crates/kesh-api/src/config.rs b/crates/kesh-api/src/config.rs index 4c189530..24a46557 100644 --- a/crates/kesh-api/src/config.rs +++ b/crates/kesh-api/src/config.rs @@ -249,10 +249,16 @@ pub struct Config { /// `KESH_ADMIN_IMPORT_MAX_MB` ; défaut 512, borne [1, 10240]. pub admin_import_max_mib: u32, - /// Story 17-3c (DC5) — répertoire où écrire le **backup automatique - /// pré-import** (`.keshbackup` de l'état courant, filet de sécurité avant - /// le restore destructeur). Lu depuis `KESH_ADMIN_BACKUP_DIR` ; défaut - /// `/tmp`. Créé s'il n'existe pas. La purge est à la charge de l'opérateur. + /// Story 17-3c (DC5) — dossier où écrire la **sauvegarde de sécurité + /// pré-import** (`.keshbackup` de l'état courant, filet avant le restore + /// destructeur). Lu depuis `KESH_ADMIN_BACKUP_DIR` ; défaut + /// [`DEFAULT_ADMIN_BACKUP_DIR`] (`/data/backup`), que les compose distribués + /// montent sur `./backup` (Story 15-13b, #552 — l'ancien défaut `/tmp` + /// mourait avec le conteneur). **Hors Docker**, poser la variable sur un + /// dossier inscriptible : sinon la création du dossier échoue et l'import + /// est refusé avant toute suppression (`AdminPreImportBackupFailed`). + /// Créé (en `0700`) s'il n'existe pas ; fichiers en `0600`. La purge est à + /// la charge de l'exploitant. pub admin_backup_dir: String, /// Story 12-5b (#194) — répertoire de stockage des **justificatifs** @@ -942,15 +948,17 @@ impl Config { None => 512, }; - // Story 17-3c (DC5) — KESH_ADMIN_BACKUP_DIR : répertoire du backup - // automatique pré-import. Défaut `/tmp`. Créé si absent (au moment de - // l'import, pas au boot). Pas de borne (chemin arbitraire opérateur). - // Story 15-11a (revue P1, B1/E-1) — vide ou blanc = absente : les - // compose transmettent `${KESH_ADMIN_BACKUP_DIR:-}`, et un chemin vide - // ferait écrire le backup pré-import dans le répertoire courant - // (`/app` dans l'image) au lieu de `/tmp`. - let admin_backup_dir = - env_nonempty("KESH_ADMIN_BACKUP_DIR").unwrap_or_else(|| "/tmp".to_string()); + // Story 17-3c (DC5) — KESH_ADMIN_BACKUP_DIR : dossier de la sauvegarde + // de sécurité pré-import. Défaut `DEFAULT_ADMIN_BACKUP_DIR` + // (`/data/backup`, monté sur `./backup` par les compose distribués — + // Story 15-13b, #552 ; hors Docker, poser la variable). Créé si absent + // (au moment de l'import, pas au boot). Pas de borne (chemin arbitraire + // opérateur). Story 15-11a (revue P1, B1/E-1) — vide ou blanc = absente : + // les compose transmettent `${KESH_ADMIN_BACKUP_DIR:-}`, et un chemin vide + // ferait écrire la sauvegarde dans le répertoire courant (`/app` dans + // l'image) au lieu du défaut. + let admin_backup_dir = env_nonempty("KESH_ADMIN_BACKUP_DIR") + .unwrap_or_else(|| DEFAULT_ADMIN_BACKUP_DIR.to_string()); // Story 12-5b (#194) — KESH_DOCUMENTS_DIR : stockage des justificatifs // importés. Défaut `/data/documents` (volume persistant Docker, PAS /tmp). @@ -1257,6 +1265,14 @@ impl LogFormat { /// Nombre de fichiers de log conservés par défaut (rotation). const DEFAULT_LOG_MAX_FILES: usize = 7; +/// Story 15-13b (#552) — défaut de `KESH_ADMIN_BACKUP_DIR` : dossier de la +/// sauvegarde de sécurité prise avant chaque import d'installation. Les deux +/// compose distribués le montent sur `./backup` (le test `transmission` de +/// `configuration_transmise.rs` exige qu'une entrée de `MONTAGES` ait cette +/// cible) ; sa valeur est fixée par le test +/// `from_env_absent_backup_dir_takes_data_backup`. Publique pour ce lien. +pub const DEFAULT_ADMIN_BACKUP_DIR: &str = "/data/backup"; + fn parse_max_files(raw: &str, warnings: &mut Vec) -> usize { match raw.trim().parse::() { Ok(0) => { @@ -1745,8 +1761,9 @@ mod tests { /// Story 15-11a (revue P1, B1/E-1) — une variable transmise VIDE ou BLANCHE /// par les compose (`${NOM:-}`) prend le défaut du code, **sans** - /// avertissement : `/tmp` (et non un chemin vide, qui ferait écrire le - /// backup pré-import dans le répertoire courant), `fr`, 12, 10, 50, 512, 587. + /// avertissement : `/data/backup` (et non un chemin vide, qui ferait écrire + /// la sauvegarde pré-import dans le répertoire courant), `fr`, 12, 10, 50, + /// 512, 587. #[test] fn from_env_empty_or_blank_vars_take_code_default_silently() { for raw in ["", " "] { @@ -1761,7 +1778,10 @@ mod tests { } let (result, logs) = from_env_with_logs(); let config = result.expect("Config should load"); - assert_eq!(config.admin_backup_dir, "/tmp", "raw={raw:?}"); + assert_eq!( + config.admin_backup_dir, DEFAULT_ADMIN_BACKUP_DIR, + "raw={raw:?}" + ); assert_eq!(config.locale, kesh_i18n::Locale::FrCh, "raw={raw:?}"); assert_eq!(config.password_min_length, 12, "raw={raw:?}"); assert_eq!(config.bank_import_max_mib, 10, "raw={raw:?}"); @@ -1788,6 +1808,25 @@ mod tests { } } + /// Story 15-13b (#552) — `KESH_ADMIN_BACKUP_DIR` absente → `/data/backup`. + /// Seul littéral voulu côté test : il fixe la valeur dont dépend la + /// persistance de la sauvegarde (le montage `./backup:/data/backup` des + /// compose). Le test précédent compare à la constante, qui ne se garde pas + /// elle-même (mutation M15). + #[test] + fn from_env_absent_backup_dir_takes_data_backup() { + let _guard = env_lock(); + reset_env(); + set_minimum_required(); + unsafe { + env::set_var("KESH_HOST", "127.0.0.1"); + env::remove_var("KESH_ADMIN_BACKUP_DIR"); + } + let config = Config::from_env().expect("Config should load"); + assert_eq!(config.admin_backup_dir, "/data/backup"); + reset_env(); + } + /// Témoin de la précédente : une valeur NON vide invalide garde son /// comportement — défaut ET avertissement « invalide » / « non reconnue ». #[test] diff --git a/crates/kesh-api/src/errors.rs b/crates/kesh-api/src/errors.rs index a69e50a3..91e5066a 100644 --- a/crates/kesh-api/src/errors.rs +++ b/crates/kesh-api/src/errors.rs @@ -458,14 +458,26 @@ pub enum AppError { #[error("Échec génération export installation : {0}")] AdminFullExportFailed(String), - /// Story 17-3c — échec de l'**import** complet d'installation (panne DB - /// pendant le restore transactionnel, backup pré-import impossible, dataset - /// source sans aucun compte Admin). HTTP 500 ; sur échec transactionnel, - /// la destination reste intacte (rollback) + backup pré-import disponible. + /// Story 17-3c — échec de l'**import** complet d'installation **après** + /// l'écriture réussie de la sauvegarde pré-import (panne DB pendant le + /// restore transactionnel, rejeu des backfills, dataset source sans aucun + /// compte Admin, commit). HTTP 500 ; la destination reste intacte + /// (rollback) et la sauvegarde pré-import existe. Story 15-13b (#576) : un + /// échec **antérieur** à la sauvegarde rend `AdminPreImportBackupFailed`. /// Détail loggé, jamais exposé en HTTP body. #[error("Échec import installation : {0}")] AdminFullImportFailed(String), + /// Story 15-13b (#576) — échec de l'import d'installation **avant** que la + /// sauvegarde de sécurité soit sur disque : transaction, verrou, lecture du + /// schéma ou des données, création du dossier, écriture du fichier. Rien + /// n'a été supprimé et **aucune sauvegarde n'existe** : le message ne la + /// promet pas, et nomme les deux pistes (dossier de sauvegarde, base de + /// données). HTTP 500, code `ADMIN_PRE_IMPORT_BACKUP_FAILED`, clé + /// `error-admin-pre-import-backup-failed`. Détail loggé, jamais exposé. + #[error("Échec sauvegarde pré-import : {0}")] + AdminPreImportBackupFailed(String), + /// Story 17-4b — échec d'envoi d'un email transactionnel via SMTP /// (connexion SMTP, auth, build du message, panne réseau). HTTP 500, i18n /// key `error-smtp-send-failed`. Détail loggé `tracing::error!`, jamais @@ -1878,7 +1890,25 @@ impl IntoResponse for AppError { "ADMIN_FULL_IMPORT_FAILED", &t( "error-admin-full-import-failed", - "Échec de l'import de l'installation. L'état précédent a été préservé.", + "Échec de l'import de l'installation. L'état précédent a été préservé ; \ + une sauvegarde de sécurité a été écrite avant l'opération.", + ), + ) + } + + // Story 15-13b (#576) — échec antérieur à la sauvegarde pré-import. + AppError::AdminPreImportBackupFailed(detail) => { + tracing::error!("admin pre-import backup failed: {detail}"); + build_response( + StatusCode::INTERNAL_SERVER_ERROR, + "ADMIN_PRE_IMPORT_BACKUP_FAILED", + &t( + "error-admin-pre-import-backup-failed", + "L'import de l'installation a échoué avant ou pendant l'écriture de la \ + sauvegarde de sécurité : aucune sauvegarde n'a été créée et rien n'a été \ + supprimé. Vérifiez que le dossier de sauvegarde est inscriptible et que la \ + base de données est joignable (les journaux du serveur indiquent la cause), \ + puis réessayez.", ), ) } @@ -3824,6 +3854,108 @@ mod tests { (parts.status, json) } + /// Story 15-13b (#576, test 15) — l'échec antérieur à la sauvegarde rend + /// un 500 `ADMIN_PRE_IMPORT_BACKUP_FAILED` dont le repli (aucun catalogue + /// n'est chargé dans ces tests) ne promet aucune sauvegarde, dit qu'aucune + /// n'a été créée et nomme les deux pistes. Témoin : `AdminFullImportFailed` + /// garde son code et un repli différent. + #[tokio::test] + async fn pre_import_backup_failed_maps_to_500_without_promise() { + let resp = + AppError::AdminPreImportBackupFailed("détail-interne-xyz".into()).into_response(); + let (status, body) = response_body(resp).await; + assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR); + assert_eq!(body["error"]["code"], "ADMIN_PRE_IMPORT_BACKUP_FAILED"); + let message = body["error"]["message"].as_str().unwrap().to_string(); + assert!( + !message.contains("détail-interne-xyz"), + "détail exposé : {message}" + ); + assert!(!message.contains("a été préservé"), "promesse : {message}"); + for jeton in [ + "aucune sauvegarde n'a été créée", + "dossier de sauvegarde", + "base de données", + ] { + assert!(message.contains(jeton), "« {jeton} » absent : {message}"); + } + let resp = AppError::AdminFullImportFailed("d".into()).into_response(); + let (status, body) = response_body(resp).await; + assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR); + assert_eq!(body["error"]["code"], "ADMIN_FULL_IMPORT_FAILED"); + assert_ne!(body["error"]["message"].as_str().unwrap(), message); + } + + /// Story 15-13b (#576, test 16) — les quatre catalogues distinguent l'échec + /// antérieur à la sauvegarde. Chargés par `I18nBundle::load` sur un chemin + /// indépendant du répertoire courant, sans toucher au catalogue global. + #[test] + fn catalogues_distinguent_l_echec_de_sauvegarde() { + let dir = + std::path::Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../kesh-i18n/locales")); + let bundle = I18nBundle::load(dir).expect("catalogues"); + // (locale, promesse interdite, négation exigée, pistes exigées) + let attendus = [ + ( + Locale::FrCh, + "un backup automatique a été créé avant l'opération", + "aucune sauvegarde n'a été créée", + ["dossier de sauvegarde", "base de données"], + ), + ( + Locale::DeCh, + "vor dem Vorgang wurde automatisch ein Backup erstellt", + "Es wurde keine Sicherung erstellt", + ["Sicherungsordner", "Datenbank"], + ), + ( + Locale::EnCh, + "an automatic backup was created before the operation", + "No backup was created", + ["backup folder", "database"], + ), + ( + Locale::ItCh, + "prima dell'operazione è stato creato un backup automatico", + "Non è stato creato alcun backup", + ["cartella di backup", "database"], + ), + ]; + let neuf_fr = bundle.format(&Locale::FrCh, "error-admin-pre-import-backup-failed", None); + for (locale, interdit, negation, pistes) in attendus { + let neuf = bundle.format(&locale, "error-admin-pre-import-backup-failed", None); + let ancien = bundle.format(&locale, "error-admin-full-import-failed", None); + // Assertion de montage : l'interdit est bien la promesse de l'ancien + // texte de SA locale — sinon il serait une coquille et le test + // passerait à vide. + assert!( + ancien.contains(interdit), + "{locale:?} : interdit absent de l'ancien texte" + ); + assert_ne!( + neuf, ancien, + "{locale:?} : texte neuf = texte d'après sauvegarde" + ); + if locale != Locale::FrCh { + assert_ne!(neuf, neuf_fr, "{locale:?} : retombé sur le français"); + } + assert!( + !neuf.contains(interdit), + "{locale:?} : promesse reprise : {neuf}" + ); + assert!( + neuf.contains(negation), + "{locale:?} : négation absente : {neuf}" + ); + for piste in pistes { + assert!( + neuf.contains(piste), + "{locale:?} : piste « {piste} » absente : {neuf}" + ); + } + } + } + #[tokio::test] async fn invalid_credentials_maps_to_401() { let resp = AppError::InvalidCredentials.into_response(); diff --git a/crates/kesh-api/src/routes/admin.rs b/crates/kesh-api/src/routes/admin.rs index 34072c4c..cef4d375 100644 --- a/crates/kesh-api/src/routes/admin.rs +++ b/crates/kesh-api/src/routes/admin.rs @@ -169,7 +169,11 @@ pub async fn full_import( } // 2c. Compat colonnes bidirectionnelle (AC12c) → 400 IMPORT_SCHEMA_MISMATCH. - check_schema_compat(&state.pool, &parsed).await?; + // Antérieur à la sauvegarde : un échec de lecture du schéma ne promet + // aucune copie (Story 15-13b, #576 — `avant_sauvegarde`). + check_schema_compat(&state.pool, &parsed) + .await + .map_err(avant_sauvegarde)?; // 3 + 4 + 5. Backup pré-import + restore transactionnel. La sérialisation // des imports destructeurs concurrents est assurée par un verrou @@ -229,21 +233,19 @@ async fn run_backup_and_restore( // Un 2e import bloque ici jusqu'au COMMIT/rollback du 1er. `_kesh_version` // n'est jamais supprimée (table système), la row id=1 existe toujours // (migration). Auto-relâché avec la transaction (robuste aux panics). - let mut tx = state - .pool - .begin() - .await - .map_err(|e| AppError::AdminFullImportFailed(format!("ouverture transaction : {e}")))?; + let mut tx = state.pool.begin().await.map_err(|e| { + AppError::AdminPreImportBackupFailed(format!("ouverture transaction : {e}")) + })?; sqlx::query("SELECT id FROM _kesh_version WHERE id = 1 FOR UPDATE") .fetch_optional(&mut *tx) .await - .map_err(|e| AppError::AdminFullImportFailed(format!("verrou installation : {e}")))? + .map_err(|e| AppError::AdminPreImportBackupFailed(format!("verrou installation : {e}")))? .ok_or_else(|| { // Row id=1 absente = installation incohérente (le boot l'aurait // refusée via check_downgrade_protection::RowMissing). Sans elle, le // FOR UPDATE ne verrouille rien → refuser l'import plutôt que de // risquer une course entre imports concurrents (review Pass 2). - AppError::AdminFullImportFailed( + AppError::AdminPreImportBackupFailed( "verrou installation : row _kesh_version id=1 absente (installation incohérente)" .into(), ) @@ -255,8 +257,12 @@ async fn run_backup_and_restore( // lecture (ils bloquent au FOR UPDATE ci-dessus) ⇒ pas de torn-read // inter-imports. `build_keshbackup` lit via le pool (connexions // séparées) ; le SELECT non-bloquant sur `_kesh_version` ne deadlocke - // pas avec le FOR UPDATE détenu par cette transaction. - let (backup_bytes, _meta) = build_keshbackup(&state.pool).await?; + // pas avec le FOR UPDATE détenu par cette transaction. Tout échec + // jusqu'à l'écriture réussie du fichier est `AdminPreImportBackupFailed` + // (Story 15-13b, #576) : aucune sauvegarde n'existe encore. + let (backup_bytes, _meta) = build_keshbackup(&state.pool) + .await + .map_err(avant_sauvegarde)?; let backup_created = write_pre_import_backup(&state.config.admin_backup_dir, &backup_bytes).await?; @@ -464,17 +470,44 @@ async fn run_backup_and_restore( Ok((backup_created, tables_restored, rows_restored)) } -/// Écrit le backup pré-import sur disque (filet de sécurité rollback). Le -/// chemin est **loggé serveur uniquement** (jamais exposé en réponse — l'admin -/// n'a pas d'accès disque sans SSH). Échec d'écriture → 500 (jamais d'import -/// sans backup réussi). +/// Story 15-13b (#576) — convertit, **à l'appel**, un échec né hors de ce +/// module **avant** que la sauvegarde pré-import soit sur disque +/// (`check_schema_compat`, `build_keshbackup`) : `AdminFullImportFailed` et +/// `AdminFullExportFailed` deviennent `AdminPreImportBackupFailed`, détail +/// conservé ; toute autre variante est rendue telle quelle +/// (`ImportSchemaMismatch` reste un 400). Forme d'appel prescrite : +/// `….await.map_err(avant_sauvegarde)?`, gardée lexicalement par le test +/// `avant_sauvegarde_branchee_aux_appels_de_l_import`. +fn avant_sauvegarde(err: AppError) -> AppError { + match err { + AppError::AdminFullImportFailed(detail) | AppError::AdminFullExportFailed(detail) => { + AppError::AdminPreImportBackupFailed(detail) + } + autre => autre, + } +} + +/// Écrit la sauvegarde de sécurité pré-import sur disque (filet avant le +/// restore destructeur). Le chemin est **journalisé côté serveur uniquement** +/// (jamais exposé en réponse). Story 15-13b (#552) : le dossier, quand Kesh le +/// crée, l'est en `0700` — **chaque niveau créé**, parents compris +/// (`DirBuilder` récursif) ; un dossier existant (montage de l'hôte) n'est pas +/// modifié. Le fichier est écrit par [`write_backup_file`] : mode `0600`, +/// sous `.partial` puis renommé, jamais par-dessus un fichier existant. +/// Tout échec rend `AdminPreImportBackupFailed` : aucun import sans +/// sauvegarde réussie (DC5). async fn write_pre_import_backup(dir: &str, bytes: &[u8]) -> Result { - tokio::fs::create_dir_all(dir).await.map_err(|e| { - AppError::AdminFullImportFailed(format!("création répertoire backup '{dir}' : {e}")) + let mut builder = tokio::fs::DirBuilder::new(); + builder.recursive(true); + #[cfg(unix)] + builder.mode(0o700); + builder.create(dir).await.map_err(|e| { + AppError::AdminPreImportBackupFailed(format!("création répertoire backup '{dir}' : {e}")) })?; - // Nom unique même pour deux backups dans la même milliseconde (le backup - // pré-import est pris avant le verrou FOR UPDATE) : pid + compteur atomique - // process-global, comme `stream_via_tempfile` (review Pass 2). + // Nom unique même pour deux sauvegardes dans la même milliseconde : pid + + // compteur atomique process-global, comme `stream_via_tempfile` (review + // Pass 2). (La sauvegarde est prise SOUS le verrou `FOR UPDATE` de + // `run_backup_and_restore` — un ancien commentaire disait « avant ».) use std::sync::atomic::{AtomicU64, Ordering}; static SEQ: AtomicU64 = AtomicU64::new(0); let path = std::path::Path::new(dir).join(format!( @@ -483,9 +516,7 @@ async fn write_pre_import_backup(dir: &str, bytes: &[u8]) -> Result Result.partial` (même dossier, donc même système de fichiers) en +/// `create_new` et, sous Unix, mode `0600` — un `.partial` déjà présent +/// fait échouer l'appel **sans rien supprimer** (on ne supprime que ce +/// qu'on a créé) ; +/// 2. écrit, puis `sync_all` ; +/// 3. vérifie que `path` n'existe pas (`try_exists` en erreur vaut échec, +/// jamais « absent ») — `rename` remplacerait une cible existante ; +/// 4. renomme `.partial` en `path` (atomique sur un même système de +/// fichiers). +/// +/// Si 2, 3 ou 4 échouent, le `.partial` créé est supprimé au mieux (un échec +/// de suppression est journalisé en `warn!` avec son chemin, sans masquer +/// l'erreur d'origine). Toute erreur : `AdminPreImportBackupFailed`, avec un +/// détail par étape qui nomme le chemin (journal seulement). Angle mort +/// écrit : la fenêtre entre la vérification 3 et le renommage 4 n'est pas +/// fermée (le nom est unique par construction). +async fn write_backup_file(path: &std::path::Path, bytes: &[u8]) -> Result<(), AppError> { + use tokio::io::AsyncWriteExt; + + let mut partial_os = path.as_os_str().to_owned(); + partial_os.push(".partial"); + let partial = std::path::PathBuf::from(partial_os); + + let mut options = tokio::fs::OpenOptions::new(); + options.write(true).create_new(true); + #[cfg(unix)] + options.mode(0o600); + let mut file = options.open(&partial).await.map_err(|e| { + AppError::AdminPreImportBackupFailed(format!( + "création fichier partiel '{}' : {e}", + partial.display() + )) + })?; + + let resultat: Result<(), AppError> = async { + file.write_all(bytes).await.map_err(|e| { + AppError::AdminPreImportBackupFailed(format!( + "écriture backup '{}' : {e}", + partial.display() + )) + })?; + file.sync_all().await.map_err(|e| { + AppError::AdminPreImportBackupFailed(format!( + "synchronisation backup '{}' : {e}", + partial.display() + )) + })?; + drop(file); + match tokio::fs::try_exists(path).await { + Ok(false) => {} + Ok(true) => { + return Err(AppError::AdminPreImportBackupFailed(format!( + "nom final déjà présent '{}'", + path.display() + ))); + } + Err(e) => { + return Err(AppError::AdminPreImportBackupFailed(format!( + "vérification nom final '{}' : {e}", + path.display() + ))); + } + } + tokio::fs::rename(&partial, path).await.map_err(|e| { + AppError::AdminPreImportBackupFailed(format!( + "renommage backup '{}' → '{}' : {e}", + partial.display(), + path.display() + )) + }) + } + .await; + + if resultat.is_err() + && let Err(e) = tokio::fs::remove_file(&partial).await + { + tracing::warn!( + path = %partial.display(), + "fichier partiel non supprimé après un échec d'écriture de la sauvegarde pré-import — ce n'est PAS une sauvegarde valide, il peut être supprimé : {e}" + ); + } + resultat +} + /// Émet `audit_log` `action='admin.full_export'`, `entity_type='installation'` /// (best-effort, snake_case `details_json` pour les JSON paths SQL). async fn emit_full_export_audit( @@ -536,3 +654,206 @@ async fn emit_full_export_audit( ); } } + +#[cfg(test)] +mod tests { + //! Story 15-13b (#552, #576) — sauvegarde pré-import : mode `0600`, + //! écriture par `.partial`, refus d'écraser, conversion des échecs + //! antérieurs à la sauvegarde, et branchement de cette conversion. + + use super::*; + + /// Test 11 — `write_pre_import_backup` sur un sous-dossier absent : dossier + /// créé en `0700`, fichier en `0600`, contenu identique, aucun `.partial` + /// restant. Assertions de montage : un fichier et un dossier témoins créés + /// par `std::fs` ne sont PAS en `0600`/`0700` — sinon l'umask rend le test + /// non discriminant. + #[cfg(unix)] + #[tokio::test] + async fn write_pre_import_backup_en_0600() { + use std::os::unix::fs::PermissionsExt; + let racine = tempfile::tempdir().expect("tempdir"); + let dir = racine.path().join("sauvegardes"); + let ecrit = write_pre_import_backup(dir.to_str().unwrap(), b"contenu-de-test") + .await + .expect("écriture de la sauvegarde"); + assert!(ecrit); + + let mode = |p: &std::path::Path| std::fs::metadata(p).unwrap().permissions().mode() & 0o777; + let temoin_fichier = dir.join("temoin.txt"); + std::fs::write(&temoin_fichier, b"t").unwrap(); + let temoin_dossier = racine.path().join("temoin-dossier"); + std::fs::create_dir(&temoin_dossier).unwrap(); + assert_ne!( + mode(&temoin_fichier), + 0o600, + "umask de l'environnement trop restrictif (077 ?) : le test ne peut pas distinguer le mode posé par le code" + ); + assert_ne!( + mode(&temoin_dossier), + 0o700, + "umask de l'environnement trop restrictif (077 ?) : le test ne peut pas distinguer le mode posé par le code" + ); + + assert_eq!(mode(&dir), 0o700, "dossier de sauvegarde créé par Kesh"); + let entrees: Vec = std::fs::read_dir(&dir) + .unwrap() + .map(|e| e.unwrap().path()) + .filter(|p| *p != temoin_fichier) + .collect(); + assert_eq!( + entrees.len(), + 1, + "une seule sauvegarde attendue : {entrees:?}" + ); + let sauvegarde = &entrees[0]; + let nom = sauvegarde.file_name().unwrap().to_str().unwrap(); + assert!( + nom.starts_with("kesh-pre-import-") && nom.ends_with(".keshbackup"), + "nom inattendu : {nom}" + ); + assert_eq!(mode(sauvegarde), 0o600, "sauvegarde en 0600"); + assert_eq!(std::fs::read(sauvegarde).unwrap(), b"contenu-de-test"); + } + + /// Test 12 (a) — un nom final déjà occupé fait échouer l'écriture en + /// `AdminPreImportBackupFailed` ; le fichier existant garde son contenu et + /// aucun `.partial` ne reste. + #[tokio::test] + async fn write_backup_file_n_ecrase_pas() { + let racine = tempfile::tempdir().expect("tempdir"); + let path = racine.path().join("kesh-pre-import-x.keshbackup"); + std::fs::write(&path, b"existant").unwrap(); + let err = write_backup_file(&path, b"nouveau") + .await + .expect_err("le nom final existe : échec attendu"); + assert!( + matches!(err, AppError::AdminPreImportBackupFailed(_)), + "variante inattendue : {err:?}" + ); + assert_eq!(std::fs::read(&path).unwrap(), b"existant"); + assert!( + !racine + .path() + .join("kesh-pre-import-x.keshbackup.partial") + .exists(), + "le .partial créé par l'appel doit être supprimé" + ); + } + + /// Test 12 (b) — un `.partial` préexistant (écriture interrompue) fait + /// échouer l'appel ; le nom final n'apparaît pas, et ce `.partial`, qui + /// n'est pas celui de l'appel, garde son contenu (on ne supprime que ce + /// qu'on a créé). + #[tokio::test] + async fn write_backup_file_passe_par_un_partiel() { + let racine = tempfile::tempdir().expect("tempdir"); + let path = racine.path().join("kesh-pre-import-y.keshbackup"); + let partial = racine.path().join("kesh-pre-import-y.keshbackup.partial"); + std::fs::write(&partial, b"interrompu").unwrap(); + let err = write_backup_file(&path, b"complet") + .await + .expect_err("un .partial préexistant doit faire échouer l'appel"); + assert!( + matches!(err, AppError::AdminPreImportBackupFailed(_)), + "variante inattendue : {err:?}" + ); + assert!(!path.exists(), "le nom final ne doit pas apparaître"); + assert_eq!(std::fs::read(&partial).unwrap(), b"interrompu"); + } + + /// Test 14 — `avant_sauvegarde` convertit les deux variantes nées avant la + /// sauvegarde, détail conservé, et rend les autres telles quelles. + #[test] + fn avant_sauvegarde_convertit_les_echecs_anterieurs() { + for err in [ + AppError::AdminFullImportFailed("d1".into()), + AppError::AdminFullExportFailed("d1".into()), + ] { + match avant_sauvegarde(err) { + AppError::AdminPreImportBackupFailed(d) => assert_eq!(d, "d1"), + autre => panic!("conversion attendue, trouvé {autre:?}"), + } + } + match avant_sauvegarde(AppError::ImportSchemaMismatch { + table: "t".into(), + unknown_columns: vec![], + missing_required_columns: vec!["c".into()], + }) { + AppError::ImportSchemaMismatch { table, .. } => assert_eq!(table, "t"), + autre => panic!("ImportSchemaMismatch doit rester un 400, trouvé {autre:?}"), + } + match avant_sauvegarde(AppError::InvalidBackupStructure("s".into())) { + AppError::InvalidBackupStructure(d) => assert_eq!(d, "s"), + autre => panic!("InvalidBackupStructure doit rester tel quel, trouvé {autre:?}"), + } + } + + /// Test 20 — garde LEXICALE (C-15-13-28) : `avant_sauvegarde` est appliquée + /// aux appels de `check_schema_compat` et `build_keshbackup` de l'import + /// (`full_import`, `run_backup_and_restore`), et PAS à celui de l'export + /// (`full_export`). Lit ce fichier, tronqué à la première ligne + /// `#[cfg(test)]`, commentaires `//` écartés ; une instruction court + /// jusqu'au `;` suivant (rustfmt peut la couper). Assertion de montage : + /// un appel de `check_schema_compat`, deux de `build_keshbackup`. + #[test] + fn avant_sauvegarde_branchee_aux_appels_de_l_import() { + let source = include_str!("admin.rs"); + let code: String = source + .lines() + .take_while(|l| l.trim() != "#[cfg(test)]") + .filter(|l| !l.trim_start().starts_with("//")) + .collect::>() + .join("\n"); + let fonction_englobante = |pos: usize| -> String { + code[..pos] + .lines() + .rev() + .find_map(|l| { + l.strip_prefix("pub async fn ") + .or_else(|| l.strip_prefix("async fn ")) + .map(|reste| reste.split('(').next().unwrap_or("").to_string()) + }) + .unwrap_or_default() + }; + let mut appels: Vec<(&str, String, String)> = Vec::new(); + for ident in ["check_schema_compat", "build_keshbackup"] { + let motif = format!("{ident}("); + for (pos, _) in code.match_indices(&motif) { + let avant = &code[..pos]; + if avant + .chars() + .last() + .is_some_and(|c| c.is_alphanumeric() || c == '_') + || avant.ends_with("fn ") + { + continue; + } + let fin = code[pos..].find(';').map_or(code.len(), |i| pos + i); + appels.push((ident, fonction_englobante(pos), code[pos..fin].to_string())); + } + } + let compte = |ident: &str| appels.iter().filter(|(i, _, _)| *i == ident).count(); + assert_eq!( + (compte("check_schema_compat"), compte("build_keshbackup")), + (1, 2), + "assertion de montage : un appel de check_schema_compat et deux de build_keshbackup attendus, trouvé {appels:#?}" + ); + for (ident, fonction, instruction) in &appels { + let convertie = instruction.contains(".map_err(avant_sauvegarde)"); + match fonction.as_str() { + "full_import" | "run_backup_and_restore" => assert!( + convertie, + "{fonction} : l'appel de {ident} doit porter `.map_err(avant_sauvegarde)` : {instruction}" + ), + "full_export" => assert!( + !convertie, + "full_export : un échec d'export reste un échec d'export : {instruction}" + ), + autre => { + panic!("appel de {ident} dans une fonction non triée `{autre}` : {instruction}") + } + } + } + } +} diff --git a/crates/kesh-api/tests/admin_full_import_e2e.rs b/crates/kesh-api/tests/admin_full_import_e2e.rs index e250f2c8..e2370c6c 100644 --- a/crates/kesh-api/tests/admin_full_import_e2e.rs +++ b/crates/kesh-api/tests/admin_full_import_e2e.rs @@ -70,7 +70,12 @@ fn test_config() -> Config { } async fn spawn_app(pool: MySqlPool) -> TestApp { - let config = test_config(); + spawn_app_with(pool, test_config()).await +} + +/// Story 15-13b — `spawn_app` sur une configuration donnée (test 17 : dossier +/// de sauvegarde inutilisable). +async fn spawn_app_with(pool: MySqlPool, config: Config) -> TestApp { let rate_limiter = kesh_api::middleware::rate_limit::RateLimiter::new(&config); let i18n = Arc::new( kesh_i18n::I18nBundle::load( @@ -585,6 +590,10 @@ async fn full_import_rolls_back_on_insert_failure(pool: MySqlPool) { let resp = post_import(&app, &keep.jwt, forged).await; assert_eq!(resp.status(), 500, "INSERT invalide → 500 (rollback)"); + // Story 15-13b (#576, test 18) — échec APRÈS la sauvegarde : le code reste + // celui de l'import, dont le message dit vrai (la sauvegarde existe). + let body: Value = resp.json().await.unwrap(); + assert_eq!(body["error"]["code"], "ADMIN_FULL_IMPORT_FAILED"); // Destination intacte : la company KeepMe d'origine est toujours là. let names: Vec = sqlx::query_scalar("SELECT name FROM companies ORDER BY name") @@ -604,6 +613,46 @@ async fn full_import_rolls_back_on_insert_failure(pool: MySqlPool) { assert_eq!(admin_count, 1, "admin d'origine préservé après rollback"); } +// ============================================================ +// Story 15-13b (#576, test 17) — la sauvegarde ne peut pas être écrite +// ============================================================ + +/// Le dossier de sauvegarde est le chemin d'un FICHIER existant : sa création +/// échoue même sous root (en CI comme en local). L'import est refusé en +/// `ADMIN_PRE_IMPORT_BACKUP_FAILED`, avec le repli Rust (ce binaire n'appelle +/// pas `init_error_i18n`) qui ne promet aucune sauvegarde, et l'installation +/// reste intacte. +#[sqlx::test(migrations = "../kesh-db/test-schema")] +async fn full_import_refuses_when_backup_cannot_be_written(pool: MySqlPool) { + let occupe = tempfile::NamedTempFile::new().expect("fichier témoin"); + let mut config = test_config(); + config.admin_backup_dir = occupe.path().to_str().unwrap().to_string(); + let app = spawn_app_with(pool.clone(), config).await; + let keep = seed_admin(&pool, "KeepMe").await; + let backup = export_backup(&app, &keep.jwt).await; + + let resp = post_import(&app, &keep.jwt, backup).await; + assert_eq!(resp.status(), 500, "sauvegarde impossible → 500"); + let body: Value = resp.json().await.unwrap(); + assert_eq!(body["error"]["code"], "ADMIN_PRE_IMPORT_BACKUP_FAILED"); + let message = body["error"]["message"].as_str().unwrap(); + assert!( + message.contains("aucune sauvegarde n'a été créée"), + "message : {message}" + ); + + let names: Vec = sqlx::query_scalar("SELECT name FROM companies ORDER BY name") + .fetch_all(&pool) + .await + .unwrap(); + assert_eq!(names, vec!["CI KeepMe".to_string()], "installation intacte"); + let admin_count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM users WHERE role = 'Admin'") + .fetch_one(&pool) + .await + .unwrap(); + assert_eq!(admin_count, 1, "admin d'origine préservé"); +} + // ============================================================ // DC11 — onboarding forcé « done » post-import (anti catch-22 #120) // ============================================================ diff --git a/crates/kesh-api/tests/configuration_transmise.rs b/crates/kesh-api/tests/configuration_transmise.rs index b49c2380..7812a8e0 100644 --- a/crates/kesh-api/tests/configuration_transmise.rs +++ b/crates/kesh-api/tests/configuration_transmise.rs @@ -208,9 +208,12 @@ const MARIADB: &[&str] = &[ "MARIADB_USER", ]; -/// Les trois montages de `kesh-api` : (cible dans le conteneur, source exigée -/// dans `docker-compose.yml` — préfixe —, source exigée dans -/// `docker-compose.prod.yml` — exacte). +/// Les montages de `kesh-api` : (cible dans le conteneur, source exigée dans +/// `docker-compose.yml` — préfixe si elle commence par `${`, exacte sinon —, +/// source exigée dans `docker-compose.prod.yml` — exacte). Voir +/// [`source_conforme`]. Story 15-13b (#552) : quatrième entrée, la sauvegarde +/// pré-import, montage fixe dans les deux compose (cible +/// `kesh_api::config::DEFAULT_ADMIN_BACKUP_DIR`, contrôlé par (T)). const MONTAGES: &[(&str, &str, &str)] = &[ ("/var/log/kesh", "${KESH_LOG_HOST_DIR:-", "./log"), ("/data/inbox", "${KESH_INBOX_HOST_DIR:-", "./inbox"), @@ -219,8 +222,14 @@ const MONTAGES: &[(&str, &str, &str)] = &[ "${KESH_DOCUMENTS_HOST_DIR:-", "./documents", ), + ("/data/backup", "./backup", "./backup"), ]; +/// Story 15-13b (AC 8 e) — sources de montage admises dans `.gitignore` sous +/// une forme **non ancrée** : `log` seul (ligne `log/` historique, aucun +/// dossier `log` versionné dans le dépôt). Toute autre source exige `/x/`. +const TOLERANCES_NON_ANCREES: &[&str] = &["log"]; + /// Le marqueur que le commentaire d'une variable de [`VIDE_SIGNIFIANT`] doit /// porter dans `.env.example`. const MARQUEUR_VIDE_SIGNIFIANT: &str = "contrairement aux autres variables"; @@ -395,8 +404,8 @@ fn source_montage<'a>(entree: &'a str, cible: &str) -> Option<&'a str> { None } -/// Les sources des trois montages de [`MONTAGES`] dans un service, par cible. -/// Une entrée en forme longue, ou dont la cible n'est pas l'une des trois, +/// Les sources des montages de [`MONTAGES`] dans un service, par cible. +/// Une entrée en forme longue, ou dont la cible n'est pas l'une d'elles, /// rougit (« forme de montage non reconnue ») ; chaque cible doit être trouvée /// exactement une fois. fn sources_montages(svc: &Service) -> Result, Vec> { @@ -458,6 +467,19 @@ fn dockerfile_fixe(dockerfile: &str, nom: &str) -> bool { .any(|l| l.trim_start().starts_with(&attendu)) } +/// Story 15-13b (AC 10 c) — la source `source` d'un montage est-elle celle +/// qu'exige [`MONTAGES`] ? Pour `docker-compose.yml` : préfixe si la source +/// attendue commence par `${` (montage variable), **égalité** sinon (montage +/// fixe : `./backups` ne vaut pas `./backup`) ; pour +/// `docker-compose.prod.yml` : égalité. +fn source_conforme(compose: Compose, source: &str, attendue_y: &str, exacte_p: &str) -> bool { + match compose { + Compose::Y if attendue_y.starts_with("${") => source.starts_with(attendue_y), + Compose::Y => source == attendue_y, + Compose::P => source == exacte_p, + } +} + /// Contrôle (T) sur les deux services : transmission, exceptions, clés /// inconnues, `env_file`, structure. fn controle_transmission( @@ -528,20 +550,35 @@ fn controle_transmission( Ok(sources) => { for (cible, prefixe_y, exacte_p) in MONTAGES { let source = &sources[cible]; + if source_conforme(compose, source, prefixe_y, exacte_p) { + continue; + } match compose { - Compose::Y if !source.starts_with(prefixe_y) => erreurs.push(format!( + Compose::Y if prefixe_y.starts_with("${") => erreurs.push(format!( "{f} : le montage de `{cible}` doit avoir pour source `{prefixe_y}…}}`, trouvé `{source}`" )), - Compose::P if source != exacte_p => erreurs.push(format!( + Compose::Y => erreurs.push(format!( + "{f} : le montage de `{cible}` doit être exactement `{prefixe_y}` (montage fixe), trouvé `{source}`" + )), + Compose::P => erreurs.push(format!( "{f} : le montage de `{cible}` doit rester `{exacte_p}` (montage fixe), trouvé `{source}` — \ le rendre configurable exige la procédure de déplacement des données de l'issue #558 (choix C83, AC 4)" )), - _ => {} } } } } } + // Story 15-13b (R4-1) — le défaut du code est la cible d'un montage : sans + // ce lien, une constante changée renverrait la sauvegarde dans le système + // de fichiers éphémère du conteneur sans qu'aucun contrôle ne rougisse. + let defaut = kesh_api::config::DEFAULT_ADMIN_BACKUP_DIR; + if !MONTAGES.iter().any(|(cible, _, _)| *cible == defaut) { + erreurs.push(format!( + "`DEFAULT_ADMIN_BACKUP_DIR` vaut `{defaut}`, qui n'est la cible d'aucun montage de `MONTAGES` : \ + la sauvegarde pré-import ne survivrait pas au conteneur (#552)" + )); + } erreurs } @@ -1852,6 +1889,55 @@ fn transmission() { ); } +/// Story 15-13b (AC 8 e, test 19) — les sources des montages par défaut ne se +/// versionnent pas et n'entrent pas dans le contexte de build : pour chaque +/// entrée de [`MONTAGES`] (liste **dérivée**, un montage ajouté demain est +/// contrôlé sans retouche), `.gitignore` porte la ligne ancrée `/x/` (seule +/// tolérance : [`TOLERANCES_NON_ANCREES`]) et `.dockerignore` la ligne `x/`. +/// Lignes comparées **entières** (après `trim`), jamais en sous-chaîne. +#[test] +fn montages_hors_du_depot() { + assert_eq!( + TOLERANCES_NON_ANCREES, + &["log"], + "seule tolérance admise : log" + ); + assert!( + MONTAGES.len() >= 4, + "assertion de montage : au moins quatre montages attendus, trouvé {}", + MONTAGES.len() + ); + assert!( + MONTAGES + .iter() + .any(|(cible, _, _)| *cible == "/data/backup"), + "assertion de montage : le montage de la sauvegarde pré-import manque" + ); + let gitignore = lire(".gitignore"); + let dockerignore = lire(".dockerignore"); + let lignes = + |texte: &str| -> BTreeSet { texte.lines().map(|l| l.trim().to_string()).collect() }; + let (g, d) = (lignes(&gitignore), lignes(&dockerignore)); + let mut erreurs = Vec::new(); + for (_, _, exacte_p) in MONTAGES { + let dossier = exacte_p + .strip_prefix("./") + .unwrap_or_else(|| panic!("source de montage inattendue : {exacte_p}")); + let ancree = format!("/{dossier}/"); + let tolere = + TOLERANCES_NON_ANCREES.contains(&dossier) && g.contains(&format!("{dossier}/")); + if !g.contains(&ancree) && !tolere { + erreurs.push(format!( + ".gitignore : ligne `{ancree}` absente (motif ancré exigé)" + )); + } + if !d.contains(&format!("{dossier}/")) { + erreurs.push(format!(".dockerignore : ligne `{dossier}/` absente")); + } + } + echouer_si(erreurs, "montages hors du dépôt"); +} + #[test] fn valeurs() { let (y, p, _) = services(); @@ -2188,6 +2274,44 @@ fn s_sources_de_montage() { sources_montages(&s).is_err(), "une forme longue doit rougir" ); + // Story 15-13b (AC 10 d) — `source_conforme` : égalité exacte pour un + // montage fixe, préfixe pour un montage variable (M14). + assert!(source_conforme( + Compose::Y, + "./backup", + "./backup", + "./backup" + )); + assert!(!source_conforme( + Compose::Y, + "./backups", + "./backup", + "./backup" + )); + assert!(!source_conforme( + Compose::Y, + "${X:-./backup}", + "./backup", + "./backup" + )); + assert!(source_conforme( + Compose::Y, + "${KESH_INBOX_HOST_DIR:-./inbox}", + "${KESH_INBOX_HOST_DIR:-", + "./inbox" + )); + assert!(source_conforme( + Compose::P, + "./backup", + "./backup", + "./backup" + )); + assert!(!source_conforme( + Compose::P, + "./backups", + "./backup", + "./backup" + )); } // --------------------------------------------------------------------------- diff --git a/crates/kesh-i18n/locales/de-CH/messages.ftl b/crates/kesh-i18n/locales/de-CH/messages.ftl index 83e78d56..0fb0e90c 100644 --- a/crates/kesh-i18n/locales/de-CH/messages.ftl +++ b/crates/kesh-i18n/locales/de-CH/messages.ftl @@ -1422,6 +1422,7 @@ export-global-souverainete-note = Ihre Daten gehören Ihnen. Kesh erstellt keine error-global-export-failed = Der globale Export konnte nicht erstellt werden. Wenn das Problem weiterhin besteht, wenden Sie sich an den Support. error-admin-full-export-failed = Der vollständige Export der Installation konnte nicht erstellt werden. Versuchen Sie es in Kürze erneut; wenn das Problem weiterhin besteht, wenden Sie sich an den Support. error-admin-full-import-failed = Der Import der Installation ist fehlgeschlagen. Der vorherige Zustand wurde beibehalten (vor dem Vorgang wurde automatisch ein Backup erstellt). Prüfen Sie die Serverprotokolle und versuchen Sie es erneut. +error-admin-pre-import-backup-failed = Der Import der Installation ist vor oder während des Schreibens der Sicherheitskopie fehlgeschlagen. Es wurde keine Sicherung erstellt und nichts gelöscht. Prüfen Sie, ob der Sicherungsordner beschreibbar und die Datenbank erreichbar ist (die Serverprotokolle nennen die Ursache), und versuchen Sie es erneut. error-invalid-backup-structure = Die Sicherungsdatei ist ungültig oder beschädigt (unerwartete Struktur oder fehlgeschlagene Integritätsprüfung). Stellen Sie sicher, dass es sich um eine von Kesh erzeugte .keshbackup-Datei handelt. error-import-schema-mismatch = Das Schema dieses Backups ist mit dieser Kesh-Version nicht kompatibel. Aktualisieren Sie Kesh oder verwenden Sie ein kompatibles Backup. error-import-client-number-collision = Das Backup enthält kollidierende Kundennummern (identisch nach Angleichung von Gross-/Kleinschreibung, Akzenten und unsichtbaren Zeichen). Korrigieren Sie die im Bericht genannten Kontakte, exportieren Sie erneut und versuchen Sie es nochmals. diff --git a/crates/kesh-i18n/locales/en-CH/messages.ftl b/crates/kesh-i18n/locales/en-CH/messages.ftl index 6f0a9005..3b6285c5 100644 --- a/crates/kesh-i18n/locales/en-CH/messages.ftl +++ b/crates/kesh-i18n/locales/en-CH/messages.ftl @@ -1422,6 +1422,7 @@ export-global-souverainete-note = Your data belongs to you. Kesh keeps no copy o error-global-export-failed = The global export could not be generated. If the problem persists, please contact support. error-admin-full-export-failed = The full installation export could not be generated. Please try again shortly; if the problem persists, contact support. error-admin-full-import-failed = The installation import failed. The previous state was preserved (an automatic backup was created before the operation). Check the server logs, then try again. +error-admin-pre-import-backup-failed = The installation import failed before or while writing the safety backup. No backup was created and nothing was deleted. Check that the backup folder is writable and that the database is reachable (the server logs give the cause), then try again. error-invalid-backup-structure = The backup file is invalid or corrupted (unexpected structure or failed integrity check). Make sure it is a .keshbackup file produced by Kesh. error-import-schema-mismatch = This backup's schema is incompatible with this version of Kesh. Update Kesh or use a compatible backup. error-import-client-number-collision = The backup contains colliding client numbers (identical once case, accents and invisible characters are folded). Fix the contacts named in the report, re-export, then try again. diff --git a/crates/kesh-i18n/locales/fr-CH/messages.ftl b/crates/kesh-i18n/locales/fr-CH/messages.ftl index 45f674bb..4b69fed8 100644 --- a/crates/kesh-i18n/locales/fr-CH/messages.ftl +++ b/crates/kesh-i18n/locales/fr-CH/messages.ftl @@ -1493,6 +1493,7 @@ export-global-souverainete-note = Vos données vous appartiennent. Kesh ne fait error-global-export-failed = L'export global n'a pas pu être généré. Si le problème persiste, contactez le support. error-admin-full-export-failed = L'export de l'installation n'a pas pu être généré. Réessayez dans quelques instants ; si le problème persiste, contactez le support. error-admin-full-import-failed = L'import de l'installation a échoué. L'état précédent a été préservé (un backup automatique a été créé avant l'opération). Vérifiez les logs serveur, puis réessayez. +error-admin-pre-import-backup-failed = L'import de l'installation a échoué avant ou pendant l'écriture de la sauvegarde de sécurité : aucune sauvegarde n'a été créée et rien n'a été supprimé. Vérifiez que le dossier de sauvegarde est inscriptible et que la base de données est joignable (les journaux du serveur indiquent la cause), puis réessayez. error-invalid-backup-structure = Le fichier de sauvegarde est invalide ou corrompu (structure inattendue ou contrôle d'intégrité échoué). Vérifiez qu'il s'agit bien d'un fichier .keshbackup produit par Kesh. error-import-schema-mismatch = Le schéma de ce backup est incompatible avec cette version de Kesh. Mettez à jour Kesh ou utilisez un backup compatible. error-import-client-number-collision = Le backup contient des numéros de client en collision (identiques une fois la casse, les accents et les caractères invisibles repliés). Corrigez les fiches nommées dans le rapport, ré-exportez, puis réessayez. diff --git a/crates/kesh-i18n/locales/it-CH/messages.ftl b/crates/kesh-i18n/locales/it-CH/messages.ftl index 18921a9f..061fb733 100644 --- a/crates/kesh-i18n/locales/it-CH/messages.ftl +++ b/crates/kesh-i18n/locales/it-CH/messages.ftl @@ -1422,6 +1422,7 @@ export-global-souverainete-note = I vostri dati vi appartengono. Kesh non effett error-global-export-failed = L'esportazione globale non è potuta essere generata. Se il problema persiste, contattate l'assistenza. error-admin-full-export-failed = L'esportazione completa dell'installazione non è potuta essere generata. Riprovate tra poco; se il problema persiste, contattate l'assistenza. error-admin-full-import-failed = L'importazione dell'installazione non è riuscita. Lo stato precedente è stato preservato (prima dell'operazione è stato creato un backup automatico). Controllate i log del server e riprovate. +error-admin-pre-import-backup-failed = L'importazione dell'installazione non è riuscita prima o durante la scrittura del backup di sicurezza. Non è stato creato alcun backup e nulla è stato eliminato. Verificate che la cartella di backup sia scrivibile e che il database sia raggiungibile (i log del server indicano la causa), poi riprovate. error-invalid-backup-structure = Il file di backup non è valido o è danneggiato (struttura inattesa o controllo di integrità fallito). Assicuratevi che sia un file .keshbackup prodotto da Kesh. error-import-schema-mismatch = Lo schema di questo backup non è compatibile con questa versione di Kesh. Aggiornate Kesh o utilizzate un backup compatibile. error-import-client-number-collision = Il backup contiene numeri cliente in collisione (identici dopo la normalizzazione di maiuscole, accenti e caratteri invisibili). Correggete le schede indicate nel rapporto, riesportate e riprovate. From 4c6b6844f78f7a073f5d5416b7abf7d924f114b8 Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Fri, 9 Oct 2026 07:50:38 +0200 Subject: [PATCH 05/12] =?UTF-8?q?docs(15-13b):=20T5-T6=20=E2=80=94=20manue?= =?UTF-8?q?l=20(dossier=20de=20sauvegarde,=20rapatriement,=20refus,=20pass?= =?UTF-8?q?age=20=C3=A0=20la=200.13.0)=20et=20PDF,=20DOCKER=5FSTART,=20rec?= =?UTF-8?q?ettes=20hors=20Docker,=20CHANGELOG=20[0.13.0]=20(refs=20#552,?= =?UTF-8?q?=20refs=20#576)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 10 +++- CLAUDE.md | 2 +- DOCKER_START.md | 1 + README.md | 2 +- .../epic-15-choix-autonomes.md | 21 +++++++++ docs/manual/fr/admin-manual.tex | 47 ++++++++++++++----- docs/testing.md | 5 +- 7 files changed, 71 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b1544f0..da653c18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,14 +58,20 @@ Le contenu est rédigé en français à destination des **fiduciaires, PME, ind - **Une opération qui croise un rapprochement bancaire ne finit plus en erreur interne ([#463](https://github.com/guycorbaz/kesh/issues/463), [#491](https://github.com/guycorbaz/kesh/issues/491), [#536](https://github.com/guycorbaz/kesh/issues/536)).** Un règlement client, l'annulation d'un règlement client, la validation d'une facture, la saisie d'une facture fournisseur et l'enregistrement des réglages de facturation qui croisent une autre opération (un rapprochement bancaire, notamment) ne finissent plus en erreur interne : le serveur rejoue l'opération, comme il le faisait déjà pour l'acceptation d'un rapprochement. Il en va de même de toutes les opérations courantes qui écrivent au journal — hors la restauration d'une sauvegarde et l'effacement des données de démonstration. Seul un interblocage répété trois fois de suite rend encore une erreur ; rien n'est alors écrit et l'opération peut être relancée telle quelle. - **Les manuels ne prétendent plus que la numérotation des factures se fait dans une transaction `SERIALIZABLE` ([#484](https://github.com/guycorbaz/kesh/issues/484)).** Ils décrivent le verrou du compteur, la contrainte d'unicité et le rejeu. -- **La configuration posée dans `.env` atteint enfin Kesh ([#550](https://github.com/guycorbaz/kesh/issues/550)).** Les deux compose fournis ne transmettaient au conteneur que les variables listées sous `environment:` — Docker Compose lit `.env` pour remplir le compose, il ne le transmet pas. Seize variables que `.env.example` proposait de régler restaient donc **sans effet, sans aucun message** : les e-mails sortants (`KESH_SMTP_*`, `KESH_PUBLIC_BASE_URL`), le mot de passe oublié par e-mail, la sortie de la démonstration, la langue, la longueur minimale des mots de passe, les plafonds d'import et de sauvegarde, et les cookies `Secure` avec `docker-compose.prod.yml`. Elles sont désormais toutes transmises, et un test empêche qu'un compose en oublie une. Le message de démarrage qui renvoyait à une variable inexistante (`KESH_ADMIN_` + `RESET`) nomme le vrai recours (`KESH_ADMIN_USERNAME` / `KESH_ADMIN_PASSWORD`). `docker-compose.yml` s'installe seul : il désigne l'image publiée au lieu d'exiger les sources. Un `.env` sans `KESH_ADMIN_PASSWORD` ne fait plus refuser le démarrage avec `docker-compose.yml`, ni avertir Compose avec `docker-compose.prod.yml` : l'onboarding `/setup` recommandé par le manuel fonctionne, et le retrait des variables après un break-glass aussi. `KESH_JWT_SECRET` reste obligatoire. **Inchangé** : avec `docker-compose.prod.yml`, les montages restent `./documents`, `./inbox`, `./log`, et les `KESH_*_HOST_DIR` y sont sans effet ([#558](https://github.com/guycorbaz/kesh/issues/558)). +- **La configuration posée dans `.env` atteint enfin Kesh ([#550](https://github.com/guycorbaz/kesh/issues/550)).** Les deux compose fournis ne transmettaient au conteneur que les variables listées sous `environment:` — Docker Compose lit `.env` pour remplir le compose, il ne le transmet pas. Seize variables que `.env.example` proposait de régler restaient donc **sans effet, sans aucun message** : les e-mails sortants (`KESH_SMTP_*`, `KESH_PUBLIC_BASE_URL`), le mot de passe oublié par e-mail, la sortie de la démonstration, la langue, la longueur minimale des mots de passe, les plafonds d'import et de sauvegarde, et les cookies `Secure` avec `docker-compose.prod.yml`. Elles sont désormais toutes transmises, et un test empêche qu'un compose en oublie une. Le message de démarrage qui renvoyait à une variable inexistante (`KESH_ADMIN_` + `RESET`) nomme le vrai recours (`KESH_ADMIN_USERNAME` / `KESH_ADMIN_PASSWORD`). `docker-compose.yml` s'installe seul : il désigne l'image publiée au lieu d'exiger les sources. Un `.env` sans `KESH_ADMIN_PASSWORD` ne fait plus refuser le démarrage avec `docker-compose.yml`, ni avertir Compose avec `docker-compose.prod.yml` : l'onboarding `/setup` recommandé par le manuel fonctionne, et le retrait des variables après un break-glass aussi. `KESH_JWT_SECRET` reste obligatoire. **Inchangé** : avec `docker-compose.prod.yml`, les montages restent `./documents`, `./inbox`, `./log` (la 0.13.0 y ajoute `./backup`, sauvegarde pré-import, [#552](https://github.com/guycorbaz/kesh/issues/552)), et les `KESH_*_HOST_DIR` y sont sans effet ([#558](https://github.com/guycorbaz/kesh/issues/558)). - ⚠️ **Action requise sur une installation existante.** La nouvelle image seule ne corrige rien : le défaut est dans le fichier compose que vous détenez, et le compose de la 0.13.0 s'emploie avec l'image 0.13.0. **Re-téléchargez le compose** et reportez-y vos seules adaptations locales (port, réseau, **chemins des montages** — un montage modifié à la main et non reporté revient à `./documents`, et justificatifs et PDF paraissent disparus : ils sont toujours dans l'ancien dossier), puis **`docker compose pull`** et **`docker compose up -d`**. Pour qui garde son fichier, le manuel d'administration décrit les gestes, fichier par fichier (§ *Passer à la 0.13.0*). **Relisez votre `.env` avant de mettre à jour** : des lignes jusqu'ici sans effet le deviennent. Avec `docker-compose.yml`, c'est d'abord **Compose** qui refuse, au terminal et avant tout démarrage, si `MARIADB_ROOT_PASSWORD` ou `MARIADB_PASSWORD` manque à `.env` (cf. **Sécurité**, [#551](https://github.com/guycorbaz/kesh/issues/551)). Kesh **refuse de démarrer** si `KESH_SMTP_TLS` ou `KESH_FEATURE_FORGOT_PASSWORD` portent une valeur autre que `true`/`false`/`1`/`0`, si `KESH_COOKIE_SECURE` en porte une avec `docker-compose.prod.yml`, si le mot de passe oublié est activé avec un SMTP incomplet ou mal formé, ou si, activé, le mailer SMTP ne se construit pas, ou encore si `KESH_JWT_SECRET` ou `KESH_ADMIN_PASSWORD` est resté à un placeholder (cf. **Sécurité**) — le message nomme la variable (`docker compose logs kesh-api`). Sans refus : `KESH_PRODUCTION_RESET` réautorise la réinitialisation **d'une installation restée en démonstration** ; les quatre `KESH_SMTP_HOST`/`_USER`/`_PASSWORD`/`_FROM` posées activent l'envoi de factures, et avec `KESH_FEATURE_FORGOT_PASSWORD=true` la réinitialisation du mot de passe par e-mail ; `KESH_COOKIE_SECURE` agit enfin avec `docker-compose.prod.yml`, de même que `KESH_ADMIN_BACKUP_DIR`, `KESH_LANG`… ; une ligne `KESH_LOG_FILE_PATH=` vide désactive désormais le journal fichier. Une valeur qui contient `$` se met entre apostrophes simples (`KESH_SMTP_PASSWORD='pa$word'`), sinon elle est tronquée. Une variable que `.env` ne pose pas, ou laisse vide, prend son défaut sans avertissement — hormis, avec `docker-compose.yml`, `MARIADB_ROOT_PASSWORD` et `MARIADB_PASSWORD`, désormais obligatoires — ; la sauvegarde pré-import reste dans `/tmp`. Un `.env` copié du gabarit sans modification ne change rien d'autre que le refus du placeholder du secret JWT et, avec `docker-compose.yml`, le sort des mots de passe MariaDB : copié du gabarit 0.13.0, où ils sont commentés, il fait refuser Compose ; copié d'un gabarit antérieur, qui posait `kesh_dev`, il fait émettre à Kesh l'avertissement « mot de passe publié ». + ⚠️ **Action requise sur une installation existante.** La nouvelle image seule ne corrige rien : le défaut est dans le fichier compose que vous détenez, et le compose de la 0.13.0 s'emploie avec l'image 0.13.0. **Re-téléchargez le compose** et reportez-y vos seules adaptations locales (port, réseau, **chemins des montages** — un montage modifié à la main et non reporté revient à `./documents`, et justificatifs et PDF paraissent disparus : ils sont toujours dans l'ancien dossier), puis **`docker compose pull`** et **`docker compose up -d`**. Pour qui garde son fichier, le manuel d'administration décrit les gestes, fichier par fichier (§ *Passer à la 0.13.0*). **Relisez votre `.env` avant de mettre à jour** : des lignes jusqu'ici sans effet le deviennent. Avec `docker-compose.yml`, c'est d'abord **Compose** qui refuse, au terminal et avant tout démarrage, si `MARIADB_ROOT_PASSWORD` ou `MARIADB_PASSWORD` manque à `.env` (cf. **Sécurité**, [#551](https://github.com/guycorbaz/kesh/issues/551)). Kesh **refuse de démarrer** si `KESH_SMTP_TLS` ou `KESH_FEATURE_FORGOT_PASSWORD` portent une valeur autre que `true`/`false`/`1`/`0`, si `KESH_COOKIE_SECURE` en porte une avec `docker-compose.prod.yml`, si le mot de passe oublié est activé avec un SMTP incomplet ou mal formé, ou si, activé, le mailer SMTP ne se construit pas, ou encore si `KESH_JWT_SECRET` ou `KESH_ADMIN_PASSWORD` est resté à un placeholder (cf. **Sécurité**) — le message nomme la variable (`docker compose logs kesh-api`). Sans refus : `KESH_PRODUCTION_RESET` réautorise la réinitialisation **d'une installation restée en démonstration** ; les quatre `KESH_SMTP_HOST`/`_USER`/`_PASSWORD`/`_FROM` posées activent l'envoi de factures, et avec `KESH_FEATURE_FORGOT_PASSWORD=true` la réinitialisation du mot de passe par e-mail ; `KESH_COOKIE_SECURE` agit enfin avec `docker-compose.prod.yml`, de même que `KESH_ADMIN_BACKUP_DIR`, `KESH_LANG`… ; une ligne `KESH_LOG_FILE_PATH=` vide désactive désormais le journal fichier. Une valeur qui contient `$` se met entre apostrophes simples (`KESH_SMTP_PASSWORD='pa$word'`), sinon elle est tronquée. Une variable que `.env` ne pose pas, ou laisse vide, prend son défaut sans avertissement — hormis, avec `docker-compose.yml`, `MARIADB_ROOT_PASSWORD` et `MARIADB_PASSWORD`, désormais obligatoires — ; pour la sauvegarde pré-import, `./backup` avec le compose de la 0.13.0 ; avec un compose gardé sans ce montage, `/data/backup` du conteneur, éphémère comme l'était `/tmp`. Un `.env` copié du gabarit sans modification ne change rien d'autre que le refus du placeholder du secret JWT et, avec `docker-compose.yml`, le sort des mots de passe MariaDB : copié du gabarit 0.13.0, où ils sont commentés, il fait refuser Compose ; copié d'un gabarit antérieur, qui posait `kesh_dev`, il fait émettre à Kesh l'avertissement « mot de passe publié ». **Exploitant de `docker-compose.yml` qui construit depuis un clone** : après `git pull`, `docker compose up -d` **tire** `gcorbaz/kesh:latest` au lieu de construire les sources — `docker compose up -d --build` pour construire, et `docker compose pull kesh-api` pour revenir à l'image publiée après un build local qui l'a étiquetée `latest`. +- **La sauvegarde prise avant un import d'installation survit au conteneur ([#552](https://github.com/guycorbaz/kesh/issues/552)).** Elle était écrite dans `/tmp` **du conteneur** et disparaissait à sa recréation — `docker compose up -d` après une modification de `.env`, une mise à jour d'image —, c'est-à-dire au moment même où l'on en a besoin après un import raté. Son défaut devient `/data/backup`, que les deux compose fournis montent sur `./backup` dans le répertoire du compose ; le fichier est créé en mode `0600` (c'est un secret : condensés des mots de passe, jetons de session), écrit sous un nom `.partial` puis renommé, jamais par-dessus un fichier existant. La restaurer demande de la rapatrier en SSH (`sudo cp` puis `sudo chown`, manuel d'administration, § *La sauvegarde de sécurité pré-import*). **Hors Docker**, poser `KESH_ADMIN_BACKUP_DIR` sur un dossier inscriptible : sans quoi tout import est refusé, avant toute suppression. **Avec un compose gardé**, ajouter le montage `./backup:/data/backup` (§ *Passer à la 0.13.0*). + +- **Un import dont la sauvegarde n'a pas pu être écrite le dit ([#576](https://github.com/guycorbaz/kesh/issues/576)).** Tout échec survenu avant que la sauvegarde de sécurité soit sur disque — dossier non inscriptible, disque plein, base de données momentanément inaccessible — annonçait « un backup automatique a été créé avant l'opération », alors qu'il n'en existait aucun. Il rend désormais `ADMIN_PRE_IMPORT_BACKUP_FAILED` : aucune sauvegarde n'a été créée, rien n'a été supprimé, et le message nomme les deux pistes, le dossier de sauvegarde et la base de données. + ### Sécurité +- **Les dossiers montés par défaut ne se versionnent plus et n'entrent plus dans l'image ([#552](https://github.com/guycorbaz/kesh/issues/552)).** `./backup` (sauvegardes pré-import, un secret), `./inbox` et `./documents` (justificatifs) étaient créés dans le clone par `docker compose up` sans être ignorés : un `git add -A` les aurait versionnés. Ils sont désormais ignorés de git (motifs ancrés à la racine) et du contexte de build, comme `./log`. + - **Le secret JWT et le mot de passe admin laissés au placeholder du gabarit, ou à un gabarit entre chevrons du manuel, sont refusés au démarrage ([#557](https://github.com/guycorbaz/kesh/issues/557)).** Jusqu'ici, `KESH_JWT_SECRET=` recopié tel quel de `.env.example` était **accepté** : les jetons de session étaient signés avec un secret publié dans le dépôt. De même, `KESH_ADMIN_PASSWORD=` décommenté tel quel, ou `KESH_ADMIN_PASSWORD=` recopié du manuel, créait sur une base vide un administrateur au mot de passe publié. Toute valeur qui contient `GENERATE_ME` (sans égard à la casse) ou qui est de la forme `<…>` est désormais refusée pour ces deux variables, avant le contrôle de longueur. **Action pour une installation existante**, avant la mise à jour, le compose final en place et, avec `docker-compose.yml`, les deux mots de passe MariaDB posés (entrée suivante) : `sudo docker compose config -q && echo 'compose lisible'` doit afficher `compose lisible` — si Compose refuse le fichier, la vérification qui suit ne reçoit rien et reste muette comme un succès —, puis la vérification du manuel d'administration (§ *Passer à la 0.13.0*), `sudo docker compose config | grep -iE 'KESH_(JWT_SECRET|ADMIN_PASSWORD): .? *(<|.*generate_me)'`, doit rester muette. Si elle montre `KESH_JWT_SECRET`, remplacez la valeur par la sortie de `openssl rand -hex 32` (toutes les sessions ouvertes sont invalidées, chacun se reconnecte) ; si elle montre `KESH_ADMIN_PASSWORD`, choisissez un vrai mot de passe ou recommentez la ligne — **et**, si l'administrateur a été créé avec ce placeholder, **changez son mot de passe** dans Kesh (le refus au démarrage ne change pas un compte déjà créé). Puis `docker compose up -d`. Sans cette action, Kesh 0.13.0 refuse de démarrer, avec un message qui nomme la variable. - **MariaDB n'est plus joignable depuis le réseau, et n'a plus de mots de passe publiés ([#551](https://github.com/guycorbaz/kesh/issues/551)).** `docker-compose.yml` publiait le port 3306 de MariaDB sur **toutes** les interfaces de l'hôte, avec des mots de passe par défaut écrits dans le dépôt (`kesh_dev_root` pour root, `kesh_dev` pour Kesh) — et un port publié par Docker **contourne le pare-feu de l'hôte** (UFW) : le pare-feu recommandé par le manuel ne le fermait pas. Le port n'est plus publié (Kesh passe par le réseau interne de Docker) ; `MARIADB_ROOT_PASSWORD` et `MARIADB_PASSWORD` n'ont plus de défaut : sans eux, Compose refuse de démarrer en nommant la variable ; `.env.example` ne les pose plus (lignes commentées). Kesh **avertit au démarrage** quand le mot de passe de `DATABASE_URL` est un mot de passe publié (`kesh_dev`, `kesh_dev_root`) ou un gabarit `<…>` recopié du manuel, et un refus d'identifiant de MariaDB (erreur 1045) est désormais suivi d'un **indice**. Le manuel fait générer le mot de passe MariaDB de l'installation Synology en hexadécimal (`openssl rand -hex 32`) : la sortie base64 qu'il prescrivait contenait une fois sur deux un `/`, qui rendait la `DATABASE_URL` inexploitable. diff --git a/CLAUDE.md b/CLAUDE.md index 3cba091e..73d8f20b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -178,7 +178,7 @@ juge pas au nombre : il se juge fichier par fichier contre cette liste. KESH_TEST_MODE=true KESH_HOST=127.0.0.1 KESH_COOKIE_SECURE=false \ DATABASE_URL='mysql://kesh:kesh_dev@127.0.0.1:3306/kesh_e2e' \ KESH_JWT_SECRET='<32+ octets>' KESH_ADMIN_PASSWORD='<12+ caractères>' \ - KESH_PORT=3000 KESH_STATIC_DIR=frontend/build \ + KESH_PORT=3000 KESH_STATIC_DIR=frontend/build KESH_ADMIN_BACKUP_DIR=target/kesh-backup \ cargo run -p kesh-api # terminal 2 diff --git a/DOCKER_START.md b/DOCKER_START.md index 892b2be7..4d717c5c 100644 --- a/DOCKER_START.md +++ b/DOCKER_START.md @@ -194,3 +194,4 @@ docker compose up -d - Les données sont persistées dans le volume `kesh-mariadb-data` du compose, que `docker volume ls` affiche `_kesh-mariadb-data` (préfixe du projet Compose, par défaut le nom du répertoire) +- Dossier `./backup` : créé par Docker au premier `docker compose up`, il reçoit la sauvegarde de sécurité prise avant chaque import d'installation (fichiers `0600`, propriétaire `root`). **Secret**, ignoré de git ; à purger (une sauvegarde par import). Restauration : manuel d'administration, § *La sauvegarde de sécurité pré-import*. diff --git a/README.md b/README.md index 23aa3e68..2fe1764c 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ npm install npm run dev ``` -L'application est accessible sur http://localhost:5173 (frontend dev) et http://localhost (API en mode Docker, port 80 ; en mode `cargo run` natif sur Linux non-root, lancer le backend avec `KESH_PORT=3000` ET le frontend avec `KESH_BACKEND_URL=http://localhost:3000 npm run dev` pour aligner le proxy vite). +L'application est accessible sur http://localhost:5173 (frontend dev) et http://localhost (API en mode Docker, port 80 ; en mode `cargo run` natif sur Linux non-root, lancer le backend avec `KESH_PORT=3000` — et `KESH_ADMIN_BACKUP_DIR` sur un dossier inscriptible, sans quoi tout import d'installation est refusé — ET le frontend avec `KESH_BACKEND_URL=http://localhost:3000 npm run dev` pour aligner le proxy vite). ### Image Docker (production) diff --git a/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md b/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md index 2708bb7f..aac0fafe 100644 --- a/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md +++ b/_bmad-output/implementation-artifacts/epic-15-choix-autonomes.md @@ -5628,3 +5628,24 @@ l'import (#458–#461). - **Retenu** : faire au T0 ce qui ne dépend pas du démon (`docker compose config`, client seul : graphie et contrôle rouge mesurés ; `make fr`), écrire le manuel selon la fiche, et **retenter** le rapatriement au T10 ; s'il est toujours impossible, l'écrire au Dev Agent Record comme **non mesuré**, à rejouer par l'orchestrateur ou en recette. `sudo` y est joué par un conteneur root (même effet de droits), ce qui sera dit. - **Écartées** : redémarrer `dockerd` (emporterait `kesh-mariadb-dev` et les bases des autres agents — interdit) ; attendre sans fin ; déclarer le geste vérifié par raisonnement (« une hypothèse éliminée par raisonnement n'est pas une hypothèse testée »). - **Réversible** : oui — la mesure se rejoue à tout moment. + +## C-15-13b-2 — 15-13b (T6) : `CLAUDE.md` — seule la ligne de commande de la recette change, pas la mention de date + +- **Contexte** : l'AC 16 a prescrit deux modifications du `CLAUDE.md` (la variable ajoutée à la recette E2E, et une mention « `KESH_ADMIN_BACKUP_DIR` ajoutée le … » accolée à « vérifié le 2026-08-04 »). La consigne de l'orchestrateur pour ce développement dit : « ne change QUE la ligne de commande de la recette E2E, rien d'autre dans ce fichier ». +- **Retenu** : la consigne de l'orchestrateur prime sur la fiche — `KESH_ADMIN_BACKUP_DIR=target/kesh-backup` ajoutée à la ligne `KESH_PORT=3000 KESH_STATIC_DIR=frontend/build`, rien d'autre. La date de vérification reste celle du 2026-08-04 ; le Dev Agent Record dit que la recette modifiée a tourné au gate E2E. +- **Écartées** : appliquer l'AC 16 a à la lettre (contrevient à la consigne). +- **Réversible** : oui (une phrase à ajouter si l'orchestrateur le souhaite). + +## C-15-13b-3 — 15-13b (T6) : la phrase « deux gestes » du CHANGELOG devient un renvoi sans nombre + +- **Contexte** : AC 13 c — la phrase partagée avec la 15-13a (« le manuel d'administration décrit les **deux gestes** ») est réécrite par la première fiche mergée, soit avec le décompte par fichier, soit par un renvoi sans nombre. +- **Retenu** : « décrit les gestes à faire, fichier par fichier (§ *Passer à la 0.13.0*) » — un renvoi qui n'a rien à recompter au rebase de la 15-13a ; le manuel, lui, porte le décompte (« Trois gestes pour chacun des deux compose » avec la 15-13b seule). +- **Écartées** : « trois gestes pour chacun » au CHANGELOG (à recompter par la seconde mergée, conflit certain sur la même ligne). +- **Réversible** : oui. + +## C-15-13b-4 — 15-13b (T5) : la brochure reste telle quelle ; le nom du fichier est décrit, non écrit en entier + +- **Contexte** : AC 11 n (brochure `:398`, « restauration sans accès SSH ») et AC 11 e (nom `kesh-pre-import--….keshbackup`). Écrit d'un bloc dans la prose, ce nom insécable produisait un `Overfull \hbox` de 155 pt ; deux autres débordements (14 et 18 pt) venaient de la ligne `KESH_ADMIN_BACKUP_DIR` de la liste des variables et d'une incise ajoutée au premier geste. +- **Retenu** : brochure **inchangée** — elle vend l'export/import par l'écran, qui reste sans SSH ; l'exception de la sauvegarde pré-import est dite au manuel d'administration (ouverture de la section). Le nom est décrit « commence par `kesh-pre-import-` (suivi de l'horodatage) et finit par `.keshbackup` » ; `\sloppy` sur l'entrée de la liste ; l'incise « sous `environment:` » du premier geste retirée (la phrase d'ouverture le dit). Résultat : 55 `Overfull`, la même liste qu'avant, aucun nouveau. +- **Écartées** : nuancer la brochure (promesse vraie pour ce qu'elle vend) ; garder le nom entier avec des `\allowbreak` (rendu haché, contrôle aplati fragilisé). +- **Réversible** : oui. diff --git a/docs/manual/fr/admin-manual.tex b/docs/manual/fr/admin-manual.tex index 330cac4e..369e6e56 100644 --- a/docs/manual/fr/admin-manual.tex +++ b/docs/manual/fr/admin-manual.tex @@ -193,7 +193,7 @@ \subsubsection{Changer le port d'écoute (conflit port 80, ex. Synology DSM Web \item \textbf{Container sur une IP dédiée (Docker macvlan/ipvlan)} — configurer le container avec sa propre IP via réseau \texttt{macvlan} ; le mapping host devient inutile (le container répond directement sur son \texttt{IP:80}). Procédure spécifique à votre infrastructure réseau. - \item \textbf{Mode \keshcommand{cargo run} natif (hors Docker) sur Linux non-root} — Linux n'autorise pas les utilisateurs non-root à bind un port \texttt{<1024} (\emph{security feature}). Dans ce contexte, set \textbf{obligatoirement} \keshcommand{KESH\_PORT=3000} (ou un autre port \texttt{≥ 1024}) dans votre \keshpath{.env} local. Alternatives possibles mais déconseillées : lancer avec \keshcommand{sudo}, ou ajouter la \emph{capability} via \keshcommand{sudo setcap 'cap\_net\_bind\_service=+ep' target/debug/kesh-api}. + \item \textbf{Mode \keshcommand{cargo run} natif (hors Docker) sur Linux non-root} — Linux n'autorise pas les utilisateurs non-root à bind un port \texttt{<1024} (\emph{security feature}). Dans ce contexte, set \textbf{obligatoirement} \keshcommand{KESH\_PORT=3000} (ou un autre port \texttt{≥ 1024}) dans votre \keshpath{.env} local. Alternatives possibles mais déconseillées : lancer avec \keshcommand{sudo}, ou ajouter la \emph{capability} via \keshcommand{sudo setcap 'cap\_net\_bind\_service=+ep' target/debug/kesh-api}. Hors Docker, posez aussi \keshcommand{KESH\_ADMIN\_BACKUP\_DIR} sur un dossier inscriptible par l'utilisateur qui lance Kesh (son défaut \texttt{/data/backup} ne l'est pas) : sans quoi tout import d'installation est refusé (\S\ref{sec:backup-ui-keshbackup}). \end{enumerate} \begin{keshtip} @@ -768,7 +768,7 @@ \subsection{Variables d'environnement complètes}\label{sec:env-vars} KESH\_BANK\_IMPORT\_MAX\_MB & Taille max fichier bank-import (CAMT.053/CSV), MiB. Borne [1, 100]. & \texttt{10} \\ KESH\_ADMIN\_EXPORT\_INMEM\_MB & Plafond in-memory de l'export \texttt{.keshbackup} (au-delà : streaming). Borne [1, 2048]. & \texttt{50} \\ KESH\_ADMIN\_IMPORT\_MAX\_MB & Taille max upload \texttt{.keshbackup} à l'import. Borne [1, 10240]. & \texttt{512} \\ - KESH\_ADMIN\_BACKUP\_DIR & Répertoire du backup automatique pré-import. Vide = défaut. & \texttt{/tmp} \\ + KESH\_ADMIN\_BACKUP\_DIR & Dossier de sauvegarde de sécurité pré-import, monté sur \texttt{./backup} par les compose fournis. Hors Docker : un dossier inscriptible. Vide = défaut. & \texttt{/data/backup} \\ \bottomrule \end{tabularx} @@ -824,8 +824,11 @@ \subsection{Variables d'environnement complètes}\label{sec:env-vars} \textbf{seul} : pointez alors vers un dossier partagé DSM (\texttt{/volume1/...}). Avec \keshpath{docker-compose.prod.yml} (installation Synology de ce manuel), les montages sont fixes --- \keshpath{./inbox}, \keshpath{./documents}, \keshpath{./log} -dans le répertoire du compose --- et ces trois variables sont \textbf{sans effet} -(une version configurable est suivie par l'issue \#558).\par\noindent +et \keshpath{./backup} dans le répertoire du compose --- et ces trois variables sont +\textbf{sans effet} (une version configurable est suivie par l'issue \#558). Les deux +compose montent en outre \keshpath{./backup} sur \texttt{/data/backup} (sauvegarde de +sécurité pré-import, \S\ref{sec:backup-ui-keshbackup}), \textbf{sans} variable +d'hôte : ce montage n'a pas de ligne dans le tableau.\par\noindent \begin{tabularx}{\linewidth}{>{\bfseries\ttfamily}lXl} \toprule \keshtableheader{Variable} & \keshtableheader{Monte sur} & \keshtableheader{Défaut host} \\ @@ -1436,6 +1439,7 @@ \subsection{Stratégie recommandée} \begin{itemize} \item \textbf{Quotidienne} : \keshcommand{mariadb-dump} complet, conservé 30 jours, stocké en local + offsite (cloud). \item \textbf{Quotidienne, avec la base} : copie du répertoire des documents (\texttt{KESH\_DOCUMENTS\_DIR}, monté depuis \keshpath{./documents} avec \keshpath{docker-compose.prod.yml} ; avec \keshpath{docker-compose.yml}, depuis le dossier de \texttt{KESH\_DOCUMENTS\_HOST\_DIR} s'il est posé) --- il porte les \textbf{PDF des factures émises}, que ni le dump ni l'export ne contiennent (cf. \S\ref{sec:pdf-factures-sauvegarde}). + \item \textbf{Avec le répertoire du compose} : le dossier \keshpath{./backup}, où Kesh écrit la sauvegarde de sécurité prise avant chaque import d'installation, est dans le périmètre du répertoire du compose --- il contient des \textbf{secrets} (\S\ref{sec:backup-ui-keshbackup}). \item \textbf{Hebdomadaire} : snapshot du volume Docker \keshpath{/var/lib/docker/volumes/kesh\_db\_data} (gel cohérent via \keshcommand{docker compose stop} puis snapshot, ou via LVM/ZFS). \item \textbf{Mensuelle} : export ZIP de souveraineté (Story 9-2b) pour chaque company depuis l'interface utilisateur. \item \textbf{Annuelle} : archivage long terme conforme OLICo Art. 9 (support non-modifiable type CD-R, ou hash signé sur support modifiable WORM). @@ -1660,7 +1664,7 @@ \subsubsection{Stratégie 3-2-1 sur DSM} \subsection{Export/import d'installation via l'interface Kesh}\label{sec:backup-ui-keshbackup} -Depuis la version v0.2, Kesh permet d'\textbf{exporter et de réimporter l'intégralité d'une installation} directement depuis l'interface web d'administration, sans accès SSH ni ligne de commande. L'export produit un fichier unique \keshcommand{.keshbackup} (conteneur compressé) contenant \textbf{toutes les données d'installation} : l'ensemble des sociétés, les utilisateurs, et les données système --- \textbf{à l'exception des fichiers} du répertoire des documents (justificatifs importés, PDF des factures émises), qui se sauvegardent à part (\S\ref{sec:pdf-factures-sauvegarde}, issue \#503). Cette fonction est \textbf{réservée au rôle Administrateur} et \textbf{inaccessible via une clé API (PAT)} — les opérations d'infrastructure ne transitent jamais par une intégration externe. +Depuis la version v0.2, Kesh permet d'\textbf{exporter et de réimporter l'intégralité d'une installation} directement depuis l'interface web d'administration, sans accès SSH ni ligne de commande. L'export produit un fichier unique \keshcommand{.keshbackup} (conteneur compressé) contenant \textbf{toutes les données d'installation} : l'ensemble des sociétés, les utilisateurs, et les données système --- \textbf{à l'exception des fichiers} du répertoire des documents (justificatifs importés, PDF des factures émises), qui se sauvegardent à part (\S\ref{sec:pdf-factures-sauvegarde}, issue \#503). Cette fonction est \textbf{réservée au rôle Administrateur} et \textbf{inaccessible via une clé API (PAT)} — les opérations d'infrastructure ne transitent jamais par une intégration externe. La \textbf{sauvegarde de sécurité pré-import} fait exception à l'absence de SSH : la rapatrier pour la restaurer demande un accès SSH (voir plus bas). \begin{keshnote} À ne pas confondre avec l'\textbf{export global per-société} (menu \textbf{Administration → Export global}, fichier CSV/ZIP) qui n'extrait les données comptables que d'\emph{une seule} entreprise pour transmission à un tiers. L'export/import \keshcommand{.keshbackup} décrit ici porte sur l'\textbf{installation complète} (toutes entreprises + comptes), pour la migration ou la restauration. @@ -1681,16 +1685,28 @@ \subsubsection{Importer (restaurer la même installation)} \textbf{Cette fonction sert à restaurer \emph{votre} installation} --- la remettre dans un état antérieur, ou la déplacer telle quelle vers un autre serveur. Ce n'est \textbf{pas} un outil pour verser les données d'une \emph{autre} installation dans la vôtre~: c'est techniquement possible, et les conséquences sur le journal d'audit sont décrites plus bas (section \emph{Conformité OLICo}). \begin{keshwarning} -\textbf{Opération destructrice et irréversible.} L'import \textbf{remplace l'intégralité des données} de l'installation actuelle par celles du fichier. À la fin, vous êtes \textbf{déconnecté} et devez vous reconnecter avec les \textbf{identifiants tels qu'ils étaient au moment de la sauvegarde} --- tout compte créé, renommé ou dont le mot de passe a changé \emph{depuis} cette sauvegarde revient à son état d'alors, et les comptes créés depuis n'existent plus. Avant toute suppression, Kesh crée \textbf{automatiquement une sauvegarde de l'état courant} côté serveur (répertoire \keshcommand{KESH\_ADMIN\_BACKUP\_DIR}, défaut \keshpath{/tmp}) en filet de sécurité ; conservez ce répertoire à l'abri et purgez-le selon votre politique. +\textbf{Opération destructrice et irréversible.} L'import \textbf{remplace l'intégralité des données} de l'installation actuelle par celles du fichier. À la fin, vous êtes \textbf{déconnecté} et devez vous reconnecter avec les \textbf{identifiants tels qu'ils étaient au moment de la sauvegarde} --- tout compte créé, renommé ou dont le mot de passe a changé \emph{depuis} cette sauvegarde revient à son état d'alors, et les comptes créés depuis n'existent plus. Avant toute suppression, Kesh crée \textbf{automatiquement une sauvegarde de l'état courant} côté serveur, en filet de sécurité --- dans le dossier de sauvegarde \keshcommand{KESH\_ADMIN\_BACKUP\_DIR}, défaut \keshpath{/data/backup}, monté sur \keshpath{./backup} par les compose fournis (voir \emph{La sauvegarde de sécurité pré-import} ci-dessous). \end{keshwarning} L'import \textbf{refuse} le fichier et préserve l'installation si : \begin{itemize} \item le fichier est \textbf{corrompu} ou n'est pas un \keshcommand{.keshbackup} valide (contrôle d'intégrité SHA-256) ; \item le \textbf{schéma} de la sauvegarde est incompatible avec la version de Kesh installée ; - \item la sauvegarde \textbf{exige une version minimale de Kesh supérieure} à celle de l'installation cible (protection anti-downgrade : ce seuil n'est relevé que par les migrations qui changent le schéma de façon incompatible) — mettez à jour Kesh avant de réimporter. + \item la sauvegarde \textbf{exige une version minimale de Kesh supérieure} à celle de l'installation cible (protection anti-downgrade : ce seuil n'est relevé que par les migrations qui changent le schéma de façon incompatible) — mettez à jour Kesh avant de réimporter ; + \item la \textbf{sauvegarde de sécurité} ne peut pas être prise ni écrite --- dossier de sauvegarde non inscriptible ou disque plein, ou base de données momentanément inaccessible : le message le dit, \textbf{aucune sauvegarde n'est créée et rien n'est supprimé} ; les journaux du serveur (\keshcommand{docker compose logs kesh-api}) indiquent la cause. \end{itemize} +\paragraph{La sauvegarde de sécurité pré-import} + +Elle est écrite dans le dossier \keshpath{backup/} du répertoire du compose (\keshpath{/volume1/docker/kesh/backup} sur Synology), sous un nom qui commence par \keshcommand{kesh-pre-import-} (suivi de l'horodatage) et finit par \keshcommand{.keshbackup} : elle \textbf{survit} au conteneur, à sa recréation et à la mise à jour de l'image. Le fichier est créé en mode \texttt{0600}, propriétaire \texttt{root} (le conteneur tourne en \texttt{root}) ; sur Synology, les ACL du dossier partagé peuvent s'ajouter à ces droits --- limite non mesurée. C'est un \textbf{secret} (mêmes contenus qu'un export). Il s'en ajoute une à chaque import, de la taille de l'installation : leur \textbf{purge} est à votre charge. Un fichier \keshcommand{….keshbackup.partial} est une écriture interrompue, \textbf{pas} une sauvegarde : il peut être supprimé. + +\textbf{Restaurer depuis elle.} L'import se fait par le navigateur, et ni File Station, ni un partage SMB, ni un \keshcommand{scp} sans \keshcommand{sudo} ne lisent un fichier \texttt{root} en \texttt{0600}. Rapatriez-la donc en SSH vers un dossier partagé, depuis le répertoire du compose : +\begin{lstlisting}[language=bash] +sudo cp backup/kesh-pre-import-<...>.keshbackup /volume1// +sudo chown /volume1//kesh-pre-import-<...>.keshbackup +\end{lstlisting} +puis importez la copie par l'écran (ci-dessus), et \textbf{supprimez-la} ensuite : c'est un secret, et l'original reste dans \keshpath{backup/}. + \paragraph{Exercices clôturés dans le désordre} Une sauvegarde produite par une version antérieure à la v0.13.0 peut porter un exercice \textbf{ouvert suivi d'un exercice clôturé} --- état que Kesh ne laisse plus se former, les exercices se clôturant désormais dans l'ordre. L'import l'\textbf{accepte} tel quel~: il restaure votre installation, il ne la corrige pas. Après l'import, l'écran \emph{Exercices comptables} affiche un \textbf{bandeau d'avertissement}, et \textbf{rien ne s'enregistre} dans l'exercice ouvert concerné --- ni saisie, ni règlement, ni dévalidation, ni modification, ni suppression~: le bilan étant cumulatif, toute écriture y changerait le bilan de l'exercice clôturé qui le suit. La réparation est celle que le bandeau indique, \textbf{dans cet ordre}~: @@ -1727,7 +1743,7 @@ \subsubsection{Importer (restaurer la même installation)} \begin{itemize} \item \keshcommand{KESH\_ADMIN\_IMPORT\_MAX\_MB} — taille maximale d'un fichier \keshcommand{.keshbackup} importé, en Mio (défaut \textbf{512}, bornes [1, 10240]). \item \keshcommand{KESH\_ADMIN\_EXPORT\_INMEM\_MB} — plafond d'assemblage en mémoire de l'export ; au-delà, l'export est streamé via un fichier temporaire (défaut \textbf{50}, bornes [1, 2048]). - \item \keshcommand{KESH\_ADMIN\_BACKUP\_DIR} — répertoire de la sauvegarde automatique pré-import (défaut \keshpath{/tmp}, y compris quand la variable est vide ; créé s'il n'existe pas). + \item \sloppy\keshcommand{KESH\_ADMIN\_BACKUP\_DIR} — dossier de sauvegarde de sécurité pré-import (défaut \keshpath{/data/backup}, y compris quand la variable est vide, monté sur \keshpath{./backup} par les compose fournis ; créé en \texttt{0700} s'il n'existe pas). Une autre valeur doit être un chemin \textbf{monté}, sans quoi la sauvegarde retourne dans le conteneur ; hors Docker, un dossier inscriptible. \end{itemize} \subsubsection{Quelle méthode de sauvegarde choisir ?} @@ -1804,12 +1820,12 @@ \subsubsection{Passer à la 0.13.0 : le compose et le fichier \texttt{.env}}\lab \item \texttt{MARIADB\_PASSWORD} changé dans \keshpath{.env} sans \texttt{ALTER USER} : « Base de données indisponible … 1045 … », suivi d'un indice (\S\ref{sec:mariadb-mdp}). \end{itemize} -\textbf{Effets sans refus} : \texttt{KESH\_PRODUCTION\_RESET} posé lors d'une ancienne démonstration réautorise, \textbf{sur une installation restée en démonstration}, la réinitialisation, qui vide toutes les données de la société ; les quatre \texttt{KESH\_SMTP\_HOST}/\texttt{\_USER}/\texttt{\_PASSWORD}/\texttt{\_FROM} posées « pour plus tard » activent l'envoi de factures par e-mail, et avec \texttt{KESH\_FEATURE\_FORGOT\_PASSWORD=true} (et \texttt{KESH\_PUBLIC\_BASE\_URL}) la réinitialisation du mot de passe par e-mail --- quiconque connaît l'adresse d'un utilisateur peut alors demander un lien ; \texttt{KESH\_COOKIE\_SECURE=false} sur \keshpath{docker-compose.prod.yml} émet des cookies sans \texttt{Secure} ; \texttt{KESH\_ADMIN\_BACKUP\_DIR} (un chemin non monté fait écrire dans le conteneur) ; \texttt{KESH\_LANG} ; \texttt{KESH\_PASSWORD\_MIN\_LENGTH} sur \keshpath{docker-compose.yml} s'applique côté serveur, alors que trois écrans gardent 12 en dur : au-dessous de 12, les écrans refusent quand même ; au-dessus, l'écran accepte et le serveur refuse ; une ligne \texttt{KESH\_LOG\_FILE\_PATH=} \textbf{vide} désactive désormais le journal fichier ; et plus généralement toute ligne décommentée de ces seize variables. +\textbf{Effets sans refus} : \texttt{KESH\_PRODUCTION\_RESET} posé lors d'une ancienne démonstration réautorise, \textbf{sur une installation restée en démonstration}, la réinitialisation, qui vide toutes les données de la société ; les quatre \texttt{KESH\_SMTP\_HOST}/\texttt{\_USER}/\texttt{\_PASSWORD}/\texttt{\_FROM} posées « pour plus tard » activent l'envoi de factures par e-mail, et avec \texttt{KESH\_FEATURE\_FORGOT\_PASSWORD=true} (et \texttt{KESH\_PUBLIC\_BASE\_URL}) la réinitialisation du mot de passe par e-mail --- quiconque connaît l'adresse d'un utilisateur peut alors demander un lien ; \texttt{KESH\_COOKIE\_SECURE=false} sur \keshpath{docker-compose.prod.yml} émet des cookies sans \texttt{Secure} ; \texttt{KESH\_ADMIN\_BACKUP\_DIR} (un chemin non monté fait écrire dans le conteneur ; sans cette ligne, la sauvegarde pré-import va désormais, avec le compose de la 0.13.0, dans \keshpath{./backup} --- avec un compose gardé sans ce montage, dans \texttt{/data/backup} du conteneur, éphémère comme avant) ; \texttt{KESH\_LANG} ; \texttt{KESH\_PASSWORD\_MIN\_LENGTH} sur \keshpath{docker-compose.yml} s'applique côté serveur, alors que trois écrans gardent 12 en dur : au-dessous de 12, les écrans refusent quand même ; au-dessus, l'écran accepte et le serveur refuse ; une ligne \texttt{KESH\_LOG\_FILE\_PATH=} \textbf{vide} désactive désormais le journal fichier ; et plus généralement toute ligne décommentée de ces seize variables. \textbf{Un \keshpath{.env} copié du gabarit sans modification} ne change pas de comportement --- les seules lignes actives du gabarit parmi ces variables valent leur défaut (\texttt{KESH\_LANG=fr}, \texttt{KESH\_PASSWORD\_MIN\_LENGTH=12}, \texttt{KESH\_BANK\_IMPORT\_MAX\_MB=10}, \texttt{KESH\_COOKIE\_SECURE=true}) --- \textbf{sauf} le secret JWT laissé au placeholder, désormais refusé, \textbf{et} les mots de passe MariaDB avec \keshpath{docker-compose.yml} : copié du gabarit \textbf{0.13.0}, où ils sont commentés et sans valeur, il fait refuser Compose ; copié d'un gabarit \textbf{antérieur}, qui posait \texttt{kesh\_dev}, il fait émettre à Kesh l'avertissement « mot de passe publié ». Un \texttt{KESH\_ADMIN\_PASSWORD} absent ou vide ne change rien avec \keshpath{docker-compose.prod.yml}, et cesse de faire refuser le démarrage avec \keshpath{docker-compose.yml}. \end{keshwarning} -\textbf{Voie recommandée : re-télécharger le compose} --- \keshpath{docker-compose.yml}, ou, sur Synology, \keshpath{docker-compose.prod.yml} enregistré sous \keshpath{docker-compose.yml} (\S\ref{sec:synology}) --- \textbf{et y reporter vos seules adaptations locales} (port \texttt{8080:80}, réseau, chemins des montages). Un montage modifié à la main et non reporté revient à \keshpath{./documents} (ou \keshpath{./inbox}, \keshpath{./log}) : justificatifs et PDF paraissent alors disparus --- ils sont toujours dans l'ancien dossier. Les commandes s'exécutent depuis le répertoire du compose (\keshpath{/volume1/docker/kesh} sur Synology) ; avec un autre nom de fichier, ajoutez \keshcommand{-f } à chaque commande \keshcommand{docker compose}. Puis \keshcommand{docker compose pull} \textbf{avant} \keshcommand{docker compose up -d}. +\textbf{Voie recommandée : re-télécharger le compose} --- \keshpath{docker-compose.yml}, ou, sur Synology, \keshpath{docker-compose.prod.yml} enregistré sous \keshpath{docker-compose.yml} (\S\ref{sec:synology}) --- \textbf{et y reporter vos seules adaptations locales} (port \texttt{8080:80}, réseau, chemins des montages). Un montage modifié à la main et non reporté revient à \keshpath{./documents} (ou \keshpath{./inbox}, \keshpath{./log}) : justificatifs et PDF paraissent alors disparus --- ils sont toujours dans l'ancien dossier. Les commandes s'exécutent depuis le répertoire du compose (\keshpath{/volume1/docker/kesh} sur Synology) ; avec un autre nom de fichier, ajoutez \keshcommand{-f } à chaque commande \keshcommand{docker compose}. Puis \keshcommand{docker compose pull} \textbf{avant} \keshcommand{docker compose up -d}. Le compose de la 0.13.0 monte en outre \keshpath{./backup} (sauvegarde de sécurité pré-import, \S\ref{sec:backup-ui-keshbackup}) : le re-télécharger suffit pour ce montage. \paragraph{MariaDB (\keshpath{docker-compose.yml}) : port fermé, mots de passe obligatoires.}\mbox{}\\ \begin{sloppypar} @@ -1831,7 +1847,7 @@ \subsubsection{Passer à la 0.13.0 : le compose et le fichier \texttt{.env}}\lab \end{sloppypar} \paragraph{Pour qui garde son fichier compose.}\mbox{}\\ -Quatre gestes pour \keshpath{docker-compose.yml}, deux pour \keshpath{docker-compose.prod.yml}. Les deux premiers, dans les deux fichiers, sous \texttt{environment:} du service \texttt{kesh-api} : +Cinq gestes pour \keshpath{docker-compose.yml}, trois pour \keshpath{docker-compose.prod.yml}. Les deux premiers, dans les deux fichiers, sous \texttt{environment:} du service \texttt{kesh-api} ; le dernier, dans les deux fichiers aussi, sous ses \texttt{volumes:} : \begin{enumerate} \item \textbf{ajouter} les lignes \textbf{nouvelles}, et elles seules --- pour \keshpath{docker-compose.yml} : \begin{lstlisting}[language=bash] @@ -1878,10 +1894,17 @@ \subsubsection{Passer à la 0.13.0 : le compose et le fichier \texttt{.env}}\lab MARIADB_PASSWORD: ${MARIADB_PASSWORD:?à poser dans .env} \end{lstlisting} Exemple abrégé : le message du compose re-téléchargé ajoute « installation existante, reprendre la valeur avec laquelle la base a été créée, jamais une neuve », que Compose affiche au refus. Le plus sûr est de recopier ces trois lignes, message compris, depuis le compose re-téléchargé. + \item \textbf{ajouter} sous \texttt{volumes:} du service \texttt{kesh-api}, dans les deux compose, le montage de la sauvegarde de sécurité pré-import --- sans lui, elle reste dans le conteneur et disparaît à sa recréation : +\begin{lstlisting}[language=bash] + - ./backup:/data/backup +\end{lstlisting} \end{enumerate} \begin{sloppypar} -Puis \keshcommand{docker compose config -q} (qui refuse une clé en double, et l'absence des deux mots de passe MariaDB), vérifier que \keshcommand{docker compose config} montre \texttt{KESH\_SMTP\_HOST} et \texttt{KESH\_PRODUCTION\_RESET}, et \textbf{aucune} entrée \texttt{ports} sous le service \texttt{mariadb} (Compose l'écrirait sous sa forme développée, \texttt{published: "3306"}), et \keshcommand{docker compose up -d}. +Puis \keshcommand{docker compose config -q} (qui refuse une clé en double, et l'absence des deux mots de passe MariaDB), vérifier que \keshcommand{docker compose config} montre \texttt{KESH\_SMTP\_HOST} et \texttt{KESH\_PRODUCTION\_RESET}, et \textbf{aucune} entrée \texttt{ports} sous le service \texttt{mariadb} (Compose l'écrirait sous sa forme développée, \texttt{published: "3306"}), que la commande ci-dessous affiche \texttt{1} --- elle compte la \textbf{cible} du montage, pas la variable, qu'un \keshpath{.env} peut poser sans montage ---, et \keshcommand{docker compose up -d}. \end{sloppypar} +\begin{lstlisting}[language=bash] +sudo docker compose config | grep -c 'target: /data/backup' +\end{lstlisting} \paragraph{Contrôle de fin de mise à jour (MariaDB, \keshpath{docker-compose.yml}).}\mbox{}\\ Il se fait \textbf{sur la base}, pas sur \keshpath{.env} : un \keshcommand{grep} sur \keshcommand{docker compose config} ne lit que \keshpath{.env}, et resterait muet alors que la base garde l'ancien mot de passe. Une fois les mots de passe changés (\S\ref{sec:mariadb-mdp}) et \keshcommand{docker compose up -d} passé : diff --git a/docs/testing.md b/docs/testing.md index 50e4b0dd..ce805c56 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -163,10 +163,11 @@ Le défaut applicatif de `KESH_HOST` est passé de `0.0.0.0` à `127.0.0.1` (Sto docker compose -f docker-compose.dev.yml up -d mariadb cd /path/to/kesh (cd frontend && npm run build) # KESH_STATIC_DIR pointe sur le résultat -mkdir -p /tmp/kesh-e2e/inbox /tmp/kesh-e2e/documents +mkdir -p /tmp/kesh-e2e/inbox /tmp/kesh-e2e/documents /tmp/kesh-e2e/backup KESH_TEST_MODE=true KESH_HOST=127.0.0.1 KESH_COOKIE_SECURE=false \ KESH_PORT=3000 KESH_STATIC_DIR="$PWD/frontend/build" \ KESH_INBOX_DIR=/tmp/kesh-e2e/inbox KESH_DOCUMENTS_DIR=/tmp/kesh-e2e/documents \ + KESH_ADMIN_BACKUP_DIR=/tmp/kesh-e2e/backup \ KESH_ADMIN_USERNAME=admin KESH_ADMIN_PASSWORD=e2e-admin-password-12chars \ KESH_SMTP_HOST=smtp.invalid KESH_SMTP_USER=e2e \ KESH_SMTP_PASSWORD=e2e KESH_SMTP_FROM=kesh@example.invalid \ @@ -281,6 +282,7 @@ Le `globalSetup` Playwright (`tests/e2e/global-setup.ts`) appelle `seedTestState KESH_TEST_MODE=true KESH_HOST=127.0.0.1 KESH_PORT=8181 \ KESH_ADMIN_USERNAME=admin KESH_ADMIN_PASSWORD=e2e-admin-password-12chars \ KESH_FEATURE_FORGOT_PASSWORD=true KESH_PUBLIC_BASE_URL=http://127.0.0.1:8181 \ + KESH_ADMIN_BACKUP_DIR=/tmp/kesh-e2e/backup \ KESH_SMTP_HOST=127.0.0.1 KESH_SMTP_PORT=2525 KESH_SMTP_USER=e2e \ KESH_SMTP_PASSWORD=e2e KESH_SMTP_FROM=kesh@example.invalid \ DATABASE_URL="mysql://root:kesh_dev_root@127.0.0.1:3306/kesh" \ @@ -301,6 +303,7 @@ KESH_TEST_MODE=true KESH_HOST=127.0.0.1 KESH_PORT=8181 \ KESH_SMTP_HOST=smtp.invalid KESH_SMTP_USER=e2e \ KESH_SMTP_PASSWORD=e2e KESH_SMTP_FROM=kesh@example.invalid \ KESH_STATIC_DIR=../frontend/build KESH_COOKIE_SECURE=false \ + KESH_ADMIN_BACKUP_DIR=/tmp/kesh-e2e/backup \ DATABASE_URL="mysql://kesh:kesh_dev@127.0.0.1:3306/kesh_e2e" \ KESH_JWT_SECRET="dev-secret-at-least-32-bytes-long-for-testing" \ cargo run -p kesh-api From 0363bdc8cf95ca7eaaa5d0b2ba2f01aeb081b92b Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Fri, 9 Oct 2026 08:21:04 +0200 Subject: [PATCH 06/12] =?UTF-8?q?docs(15-13b):=20Dev=20Agent=20Record=20?= =?UTF-8?q?=E2=80=94=20gates=20r=C3=A9els,=20mutations=2028/28,=20statut?= =?UTF-8?q?=20review=20(refs=20#552,=20refs=20#576)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- ...13b-sauvegarde-avant-import-persistante.md | 127 +++++++++++++++--- .../sprint-status.yaml | 2 +- 2 files changed, 108 insertions(+), 21 deletions(-) diff --git a/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md b/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md index 5b4ed05a..287ef5b4 100644 --- a/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md +++ b/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md @@ -1,6 +1,6 @@ # Story 15.13b : la sauvegarde prise avant un import survit au redémarrage, et son échec est annoncé tel quel -Status: ready-for-dev +Status: review N4V3V6}w9~&ZJq#fnRR%4uB&))DI`F8`>G4 z9uO!adFY>o#i}VboW;_iC7e9m|KMG4Mo0-i6M)4p==y7^5C~cWJBjZJKzru2%!(Yx zIA!_$j=V}mj&A!7#gR#s4_W_*=TX%;z*qjM<8Pa@|D(uc!c~P=ZT?N66}?iu%w%So znQ)0|Svl{Ss*-DDcLQNIKJ{=+aTwl@^P!}QkX6|kU09*`t~It21PAKAe2!})h4)*ZhMe!tFBAq8q`g`e?!t)z!oN$hBD;%bjO*Krw_)4 zT|3Y!)x=qWW3GUrs%=>vwBc^%mksT7t#L4nOm)U4HVVyw4~$~#)}2qEPr~4SqWVqH zR3gL>Q3pOpD|0=9=BFzLb@U)nZCWT~r4&fuqflpm#{&Bo(2q5ES`jV88N55c)zgI! zG23+15dbvJlAIt8{kfK+exX`9IuA6e z09VaZjxsRk${G0m{rzk7w+0py>LuX4&5e2Fv?>hU& zMwN~Op!}NRNLia2e9J`tN#BJ!?Yb@_`S`vj+5}mSNA`AJfvD|L{Ax%;{+4rdcVX`$tPxc z4Y4jC`k(i>NtZ7}UP@1qxyIeJKqjL(Pxy*eKP}3IAD`7jx%1|;4gt$)0@+(-Bm!ZS zQ@_?Yd-U-VR<{bTdBD1yl5%2dYvwH;3#b!shT#+Zo^ubiWVcxj+5S+dQyfKJZ9qhmCxC1^~KQ06>85p_K>pz|w7i3huElAkp zyBubI00LBI`pIE}M?K2#TGSqUnEq`Ro@)nyKVL2{FAjvvy;Hzc^`zhocr`q`veMw5 z+-{p$SbNfZzec>~ZmFpv#7 z5D3Qs8mh$o%CzlKzC-@?b@zD!$Mb91p~n7WEk+=IG3|N+6J&uD3400c99&W8kB*Q8 zt6bLkPpH1RB}P>66SqDFmWS-hC%=H*Z%aTC)-doo^aNZv&tGLzbqh)b_sX-l&SBKT z0BtTe4o$8839hC-IED=b+nteS&()nduCtRO zCEMbdN(zHwEKEf*)}$M7HxPDe|8#>AaJv|8P8HF~4!A#H8jD$JI!7FV?t^BXCv0tb zwZEBzq;B#5_2hZ=KECe`gsYF!0J|)XSk>^lE=UWeY3}Rv`Wag<6W3-h7isjtu2Z6i z;9p$#Rg0ZNhjg9lpNRG)(Ni2jq}YcwDv-P(Awnd>KK1)QU`Ne;IT-{K z;~HR1)A!5bxSB1o4|E7M(y3}uQ$4(jfQqvqheF3PG zF6!>r7Zi}e?q^iM#1_;oU>9Z?Ow+Z^(Qy__jaEQ+*W&?%5pF{NwR7cjK!s0#Fp2Mp z`N-3J+|PCG1(w1U%)@Z%YxVt&RvK9SF*LKVDGY*^yCEKzh3dAF0#I;{4PJ(jWDcX~ zKu6$;0?4G~A>yOU(*qLd@=9r^X@r|eg!~M;_1N@cQiizF^3~M`Sk$9^*j(ij za3XME?C-j@BZXT}^%RftL|Cm*tnDbo)vVNS*px9PlW2cUq7!bRjOaQ0PyQB|_977h zJG;s|i4>ec9rPku9}2=-#PeqdoZ5dL9LJGRuv~Qu#f+MGu8NmHNg(xTFDBoltb?fg z&c^&+>&)v|nHB;9TwBltciD`V^W$mOWL0>I<~}cv4{NA>!kw~Z?yLaea>yh*5Sy06 z!Vm-dMU(AJE38ngV*zoXSHT`Yy-%VW>O!QNmZ)l3ck&MaTZnK61SW@yVi9aG&j!n( zEV28};Q&B2#bJw`@7kb$N)bvmpmMZ$xfAi3~wOhq>U2wu(3C(+nR&ng0#%FS2O zFrghCYuSIw+(Zl-VNfA<2i+M0=ZOwau6Q|Pke22u@(O8uE28JWmsvi5K6hzb>jtF9 z+ERs0xJk%=?NZUqm7DdR#d z#YVXS?efz8R76Fe0l|fqy95K*An_)lxs})(>>>dZ!-7{YY$VxGB|3)LVp1zyOsYD35tNZ!Xo0PSTFnosI9E$Z?-eSn^NP7h%`rOhS(VB zx(**!q!8swWJC!Qf&x@}@O zHn^q&Ksqi9`<#Myja|cUP);?Q;`p$+-BxfGZcCD)FN;ROv$J@Sl{+8T+_va=e}h1D zPW>vv(^)b>sbehrCrfY8nT7GTRx_+u!tM~}#oc+6Kke6*9coAAQx*4I+B?qK<-N7L zTxG*>U^+~UDQ63aoP#2rAHz)R66aUYC{v0Bs1%gCMHNaFc{KeNYCR?a(-W;iF%4VN zmUCnj-q%h`Z$fWfDjF66zd~oM^y!H7mMZX?QxY>E&DE*JUR=c50@2Kd?Q%UMjyjyA&DenZaHEGLSQTqKMV)8 z-ckHe>nP+<^36L($$D4c-1#M}L@ysa<_wh#!@jcdOnr>3hh`NmA40|~)tdy)_gB94JZ#=Ipg1Vn2vXg(;`)9760U+rspTbO! zC|a8B&fgIY)m{x>IGIU)+&1C{Z_iZWvbfkimR}i{kk)x~!6`?u1iA(fhJ4^!fc$Bb z*|*#q#YM8844C<B?lOHYYwTY>e%tqFI~J$o)_G&%XvAyWNfU9naU zcM`1q>EQ_l_r2B7ubCL43wH$~0KELKEcBAacU4;xd2QA3B-#3pnGvyyd(2)jMDk!v z@e{OhCk<&BtPwM*{61b8dQ-JV6-;m6|U1S5ouNzeRCN%LEJZ)&rYNfSU*79v=Le$Bp1s zAoy-ugc`^lO3VPWHIN$avq83o5*bxRDM0uq_JzT8c@$Zm1lF~$rh-^6W=uHqi+?%9{(-(90-ZXojteKb0@_CuK#AR4OqmxX_1@AC zsR!>(R})H>8EdlM3YG}#=gtYwrnnVo5jx^qcmAq$3lr8b&mOp1)MqEaxg@d6EoR0l zvU9D-A{mchFYiy@R-=-70PJqrl1QYuv?A`e7IvNEvNm?UP8jOxx4b9UO@#gt+337 zJgSvJc2Reg42}R!5_0uN&^d1!Jop-&FD&_H`pgUygiGpf^+nxHH~G}IW&DZV6*=72 zdjbtkQw;F{e;KK(f6bkAuJ|M_csA29?bVAoC0ANkL_aRYckuD5MYQTBFgv0=PAMHr zIcb+Td~k}t)1663#A?0@{Xk8Q%Y?it`FvuR)H$ONWA8C6~7)#1wYUG;T7`0BkhgF9|}kR>Ep z3HlLY;4(|IRQDAk4DMA7$|JrFEAr~MdCj?gvnf4Z>HS2U@e}^9$r=@yE$y}&oD#tP z|C_AsNjv^)vUX&Qb^r-O0+WPT-5~oz|4cN|=yFTv{+}jgr6h&{{TuOw^%WTFKO_0r ze?->v{C;_cD6fbVl#~{OmxIk8VF8S*}h42V(CgOu)X@y$=Qv^ zNxnFE^kR7+PL~o%ynQ4{L&xuF;=gvGOu&MVrX#(Wk97Zr4DWPsOs{NvKWiVV*LQNs?AsaAY3XSz2i+FE& z#ANUy#n2 z3ch%T0KT3M{B!SwCOG>}V18Vvq(}GVa@u@zMn$vdg@ebZtAkt$$6y%fWwHY(^Is$o zk*t7!dkg9jliuOuWqhK|Hs~0Fir}WifM+Sqkofh=P5Hf%Ro6VSq`zrQxXRw~NO=A2 z#g8e4u5~2*n}eN|SW2b##ST^jHwU-M6DRR$aVKQADPjLkzp`!MJ)Z!0?Pd1oE9m?D{-(m^;3SeKx_YiI!Iv>8 z9sk}$SP&Wf)dBN}aEgvbo3g#9s@q*U&CIsDnvhe5*2Zr7J57cjrYRn}L$Os(*BBCz z6LXEqwpOVHnNwEMdb4O6ToFi+&{qq+N<2NS5l4p{xE6(u1=O-jERi%PXJ zD5FRgKwE1>s8^{{$0#vtjCqb3g6_N~zPdKZ$kAI*EuKNn#) zS$WE`S$d?I^W?<}0)64`S|;(&F~G}th1-G^l3dVlKDxsI5wXF!wv@6~qM!CCzeYzNRJJ!-lx*#P?=4%Tl}ljVLsW zj>4-SN8=s6&pwmMiL%piP2*p_0oIbv&vQhpYQyP+nE)Qs8^{EXOf2fveu6P7wuBsU zC=I?vjBzc~=A>PLxmBvrb5go`v4hIM52Fx_81(6;h@{#E~-7v5&)j%JtuHvyg7a9>~=X z>8kH>vVwCnj7!5VJFm9(Ts}6M1g!t?E0&|FT~I43*d%;xV~GK9{+=d3Y<=kfQl$7G zSaj7Rh}eU~yW`s5cG>_)N?o|az+7A`6_tdM^aP0+f`AV*#6G>%p4OL5*LuIi{!x~6 zVQL5MR_1auy}*vO5W_(LnZ;i06Ptwz(3jjEA*l8@Gu<6xP<<`P9AQ8zkLn@%JWE0# zJt+3@q3UJEu87DV2dpuozbfW~3zI9z{vi#Z3M(E>riu0#hu^(DinUK1St{Rg6Q#(n z^No>X@-hTv6CzIy7!Z~@kct|-b?Cnzp6IR*+CC3tCF>Lx^OK>e+um3{2MwU@h;7{q8R*CIORvmM5>MDgrMdNQgNNmCcw zP056=uHr-l&ys!I3aY6FXR;*gsSiVb@w;wuO+EolT>}x=-)y%0ZT8SV zUx6_D_Ebue9=x|=>npAz3@HqaX=-9S%OsBB7-Wtw8aDBMfm(kw4O2}**)gRZn5?UW zB^{{$lqzS8XNi_vqQ0eqjf*b@ertlSv$NK*llEVZi*qgkmcwx%P>|gX+VVO(FXpsY z%DDCx+TeHePLoavdM-HF(T;U>i=sEOUffo$TrVLO;8+U<>o=+lb}nOe#Q^we{W#8w z*G5hX4$J+Z44|Io^3&4F5M^_1xE3_0Uhf*4TRyCLf(9%?zAT-;V+N>>yU)d%R=vLa znAQ(a)?*F;nhjESB$zjCV>5JLf*Wd>679#CvbLB7!PNM%eNn0M^7yOeab3TJ1uco7Kc(3@iWcwY^>;$O! ztu=fsEHV*^myctEU0yqHC#XV0MnqmdO! z)Fp&;`yV=3xcH58Bvqj`2p8kQjpKyzw>hK$g1-%sXgD+#iiCG>$iWHYh$xj+_Sdw% zV~b3J!NJE*P^7p@5S)3~A+B^Q6lN{!GoPa|czSwscS}Nt=!iZo`() zzD_bLFlyF#c`qZ`9oaeHhrtAA?b$gCQn2r=$@abiLDvkp!G zdx`1KUND#k1I{G<21?zHIJ{Yc6Q!Z8AnYr2*jftPe`0r6rV}h(#pKftWWBliIKH_V@GJ70 z2=LPHiMx)JV%qRmUl%bk@|8vMc|;uV((&)p<=Yo63G+Ez#xYAOK$D=7)* z`z&CpH21f&*6Whg1CJ+s5XJc5RtMaCP=<;jTAd{epQZpfCLI$DZS-xx`#Ic5e@tW3 z!+oC5tA9S9*b&>yLj2aaKfSJcbDB( z4)?@KdnYFREwVxn*YjfTptY@WPj1h(!b@ZHX?PMMp+6>k_{sGD9=un2GVurP$p3mmr-+&pmHVicuSi5)LiSmY z^rJ?b@g0{0<_vEi6@`*gXiHrSjdE+l)A?O?V-&sK_XjeUf81uNzY%i=r`KjbW_v!T zG5z=Q>K1l#LGB&w;C?vPUH>HsNBb{zTRVAOdH$sgkNtqctK1#l)0`j#e88&5Ox=%{ zGK_xmyu%>k95TFt%dzCS)Hbcz+3U^;!2QSbkJuL)9ceyFVC%#BYYz{>EO-B%I5q04 z#C7-K3VJSb+Dj?`GQj``LNnSX=3HmRC%pEq=tYYZqC zQ1siFE)9Sct*<|cA_}RS@qeiL2H;$RX6;xfwr$(CZTrNw-q^Oy6Wg|JpV&@L{O8`U z{;K;`ZB6Y|Z%xna&TRGc^z-yzZY|SQ9gGf8j~);yW1LuRQuut-$S<`e-;$H7W{~yV zK37QQZe^{-fBcqO=gqpk$Ezc_&3$i)JB4k7Wc%?2`Z}G8pzS0hUi%(YgZU{)QZqf;smg9K1bom+v+4MgYY$MDD zcM6Lq2?ZB*T{z%UTtd>wy+~{1qGM1ska*(J#Cjf?TU4M2AaZ|bb|XDRYFMc974HL@ z&T@XQ0>q40;T~9@uqUG!K9K~4$JQs$xHsrSAY&%}A&WPs3*r6E`rA0TjCQnuDes&$ zwS*-1h}`Y>_;~h?oMRZe7XQY{dt!k4B+n?pDZgX{Mr$Cy5PdDw82)DR^t&Ya-2GgV zJj9}NzP}rJP2kf8ERPt+RNP?#TYlIphAvmTeb<+J$GVk5 z@G6e3w=>zwa*qpr$A@;cvtX8OuFLvY8mXY3!|<2HM*f@YY~1>hEn?2v?}5cFo+1!s zHRY!AEP*G`9lIn79F~td$AK!eSh~BZ!r(dwH(^~MOxmG2;s}RV5|o-(j1ZesN%qjU zB7n@GQlXav`a}wLR%C1heIcueAc2?Z9~YoaYvy$|2@EY)+D*tZ39@inGJn$DcjRXW zI`0@~0#7Iw+{`2K191xA;c3{$T##xmG8)5Vn$d(MmgC8YoRT(eweTgjmIR!NIpFA< z{=qKCsk9d5!r2FiRp+-w6azj%#`-fkY5&@I$omuevD>5ENIl#gF1dEoa@18@5m)Ef6LS_feA3d8pOu- zm^DTkoOI$JFl;ThE?yRn3~}}g!~uN%^*KTSR16JmS#3K z+dY-Nzuwlt1|wF%Ug>)KN^Ac9-2lAQVlc#YF5pgNhaq6LSx@ZOw$Ea*{x;ag3E6&W zz1CY;w{!COg8`?juDSn%!wJJ@ZQ(nm&cs5o%_ z1R1djyr0V!e8?tRtx2Wfi*s%sxwy5eA_onPvz!K&#>7ERi>i0z4-=zY5?m&9bdVKT&!JW(M)NkzoK#g)Tl|%$dRO-+TYRWj$*`54-&|7)l5xe*Dm0oq~Qu z4)Bzz%tP8_9arw!&YT;l*^tRv==vZguV zRQWN=TkFg^KLYibvBwy+GPb0_Kbjej0cvqXz;w0Mzz$nC5uniEK#c)yHeiggrl>$I zE?cc2V}qFBK#&JR-ZX@Zm?Qds(jge7-DQJzMP8gUZ8nn`IEWXs*T*j_vJ%kIp=R*5 z)+5bHl}<*lb}}}ITMYsGcFfIp(e}D#7hzJa+$AZr^wkehlLz#P1#~*viw%E6u{Q2L zu>}X`a5l?9`rxs+39XGK#16ka&H|6#n#k87AL}w5IplzxL|37nCq}TbVk+kP;IB*~ z_F#f8b&w#AwKU1*=t#zKkn;cDRwG>^3M`hty>j{9tZcUDew_pS)UBZByrVeEF|Y*d zH->q*F*Nn3=y6s~5!};Y`)msAbMvL6!gPk>%&BXxkf%-t%jh{7I4EIqzkE}t^z=|t zw`DKL_N-@>w!&`(mr&IF9AbpFbQ0FQQ`;g3LU~CPZ)ku<*ufCJ2Dp)?pL4eAeUWT$ z>St^k@HbnFC=LObVkr<6cv_+}x4~-qEOz#jE0Qth#V+mSMK%4#wTQKI8wDivSkyd| z!PlFwZN7WLgRgC^Bv7RqY=NO?%=DVTy`ug#L?Dyg<%l15ZE8F&p#l(UkNNiDeGbDO zNDSNUvjyu*4For}J!`@YV@J2cVHZ)Bb(an1EsF6QF`j@UwZCNHq3bzPkty zprTdHTFg@3sC(Y4s%dSvd?FIa3D8Z@m+~s7i)@VIxl5dUb__~}CtbXO6vnE|{o1s} zgW2L~axny??h#NliO;T*KMRp-kOEq0j`@_#_SYc~B%|k$?$~(fP*xazV%PU3*L`IL zTkzY-Vnb<7w~SSYEx1V2URIN#u}Woc1T6S{-EGxr zVR0X#hPiU;T1ZMYR!-LK+3m-M>fu?AIuNXK-k<}nE!q!))C{*L;uD?j?pW2NSZB|3@ykKYKmdZwg=&sf z-m9LkUCA9_CiJThR1@9(XCCr^Zj}~hiAk};mQidW8Fr}qf7B{OYZ-7>)8%!-M>pdfe%OV+|hB|JpwG?#!j*b zW3j&B))sa_co>HCW3f?A_L+ET$7?@TD1e*MC|RJ+Px0sd_h<-jf>;;)D9q5dXPFU! zYc?{%swoCsyQ*cs$~j8*0x=LMU_CsnF-Esr*%q(UA|0N61n8~`kO~IjTng{-b9UUqYz%mfK zKjv|wiCDaBvssk}idQH6XD~buzD2n1lT)C45%eg2gLcM>0%J+1_wXOe)Y4(VW8xlK zbVpnNl0V=5q_uYR^dqgyKzXipy1Mj>P#N+n<6o}$1`b9>$S|WEnmT#UtHBaxy~3>@ zHuz^)Z$ub?_j3ejkpnV*tr$fCf(#V8H%mKKwi4p2Z4gQ?x?s(aG`+Y-=}(Zf?rnFU zJxZCWPU-$hEPl3inapFCZ4dujc-H#-z^49=mGJ7|AuSH)<9hda0E6ItK|8+Ke8QVS zz$~TTc^%#KGlb&u(yb5nW!d~Vl==fV<0nE#K{ykK-k6;g?7 zTTegj5xtL7%rYQ&?0kH(1$XjmHm+*vIeg#p=}zM$k1>X_04w!h6>iD1pBrBix~a>k zQ3<5h9_8@V)>VZ7JAp5lh5cLDtxcm1K?G?M55 zo}0T?cR)`U)9w(**O%G*KE+z1bbsj81Ot=L+!5_viiW)hNSkI)Snvyy0OVI2VrJ>Inu8$=%(bW`%zR6dFDJ_YZYo1o#2*f$-g`nR!B1y1rl{TBhXH|fJy)M5g12LQJZ)&1br#@PmAEUm<-Vc^w=Uwn7? zwMp#9PHttx9>x9dk?wAZ4O}?bRy(&fR4Vh$t{I7#db<+llX~tvS;Z6Pi^j18Qc*`*yv?4*>1J%ydNQma9AsgSBaYP|_C z3!H826vGEiA1iPLJ{GaOJ<(o*caZ_swKkDA|H$VY&a0B`>+)38<{T=`T`DKuq=RhO zrn~OHwAo7=UGKN4G15GeyHyDcM_*vrv;hYCEa6-Q*Z}l)1L^E`V(YT*aTouZ%`SV} z`Unge(?*l>Ml&jmB-t|ZQv$zxQf)X3uES?I$^*p!%|A-gr~;*k6FpH>uvrH&PMx-3 zf;9;k3mL&G1vz2c#gtS7?fCom1C6`+O60R2ApKMg9w^D62ER$yr{(7pV>=YbN&yp5 zuB3K5efR0B>b}nF!RNK0l`c41$@ym}7fzeq*7XH-ZgnYhdu@|B)~zI4)$Y@bwf%m> zvqM@s4r-Yr*;wJiN8@GW@p|_t7aPF_|JViGQ+h75a@}Ot0s(#K*;uqqG>DU~*hc^0 zr88~XD66q}BONDPN?dc&P4L92xd4F!`pXyEuAE7+bK=^q%u%PwA#3Kh1yTC6?``tX zAQL(B4=!CwrfNVSSt#CjinjU6{xv;dRLZ`fHF~GwvUn^+Bo3t*Zt5p@?oEZVm5@=_ z=r+%K0dYyfroWod_*k9k5)QT1<>JN%8a0Y;5+qP>wtArHPL*J&G0zfU)c{THn6VPH zTJAXkm=Wa8eejk|HXA|4ba2u2FVj4!BY5{^8SKu8q3O_xoBW~OGFB$Lv3;8$`vI^p z%H^JYE;lbP##eT#PSFuHX@g+QnsKEeY4g*lf!B`Unt8=lBbx0E<%r8abd{(feG;t= z&=V03nvkQ;>gq5qRo2_HjDRrVN_3$m&dRPUp{24Ky!on?K&@0JDKE%h_OpFMD>oB| zh+N?YrIxoqORY>tRgUB+kUk%@W+H>ixuUO@W;j%BlZXntgUvL8jWnH0jrN8iAj=S~ zcd%20P*Q9&OdISrCjR(f6fC1d@)gJachCCIm^yx3+%x@_>Jr)D-T=r;fmngu8gX)} zMnD|86o{eBVPgGw)0(LhYlKfqcdF!r4&&nBFL!DX(r%b^j5iC~{IU$x1z5!PsMhuw zN-2MpQ%09cnjASX+selwC|UFCfQBjm9^EX;OCY8y{~zR@3Cv2`huqjuj9B8t*!p>r zPk|(#fn|Un_JJ9406=xZSjgjMeziT{W=*2J1%uj*pk%q`<2z~*iXtdONsjwi1=h%7 z`7v7a(GTl7o@O=UEs3|5BHgNY)EojP37d6%P|d?=Q4J^&}oon>v^wlR~3XehpDC&&`%osj@PcIN!dUKE(r|&?D(w0pU*Z z7p))cHXBf_BZvDJC2ip^rML9{C)CZ;9L_eI!Xd$&LX~G}!6Q$C7C`$zPdw7m(VK*! zL8a5e67qD04{-Y0EPb{(c$Pqkmb0KVe|L3`t?}u7>b=_CoXsm=CBPF_B_bz{W=W^q z>W`OY87oJAb?G$Rk7MKurSpEIy^m}dNB8TfkvX*R;gfZP9M6pe$d0L#`RgKHbC|4gU z_3VdtjTG#+jgo&~_j8ka z=XF#sSpdk3$STPsblQDEk1>=f50yj}2Qb&W`L-AGpsORgz5649zqp^sA{;-5ux%YR za)V_)Z|McC z+v-{I017bM~EaCX0s-xD{U9L9BFKif3z{$J7_RvzWr>aE9LG+O#Co5OI^@&$}gKk zJpn$3B7rdD)5!(kQ{<#7h(sNY+r1RI?eroHZP;{9s_m%v$snl84QLBn$W@cNs?;3b zn#q~J=rFu`W^$JF75yN@0RcJ7`e75)+BV;yYxN(vo@3ozx@6#U zZQeGc13fV}ikBIgHa|P6f&77Mfl<12Is-ViE-xAz)M_qwMxy1b>Xmt3x?GoGlGuAr z()am_j#?$L_JTDBUfYG05+Lv|krkw6qx(YNR`u|GNIVX87@Ax48s$cety8RX>Z4oS zE^r3pI5nY|Ux*s_Q!+IEt};`H()mYDTBD?FJoQ`EB0vP-4>7o<1j6use`Fz4dk@&D zT-?&2jDB8eIZ$sAB=7UuBxVf3oxw`jFSp9-&K!F)p6d@F^vH~7 zw0QHxf{IP#hm$mMu@PA>=E^%5XU5;x`CUi8*BAseqZ+sGzJ5T`;tlsn7}v2YhdsH1 z+|i91T4?7!FxZ~M`jir)3MNe%g-AB$Bs~Ire!69Xz~Lxa$w6l~m{6R`T?+92;ZRTr zz@mwJI9!wpBUV^0sc(ptV+HV*3<-_k=Q%hA7bFEdI2Uu_ObC{$3C z7;7|e6{`jR<>h@z#X65Mvhxqmcybz&C?;dmBBUpQ0s@|lIbtMpOlLNDRwbj=6ZAT} zL6r6+#ocWQ@_-)7UoJlWpE`io^Blaf%jOzsSzQ*mDHuWS*(;8xW{>g=uw@V3W+bmx z{3R4EZ{`(#kRAG1%Q_XjOfPb9AgOoX!5s|Zb<6({fvh$8&(IhJk=}}8#wj%^ysH3V zFRb_(4?Va|j6?x;(&Z{WFJ{zMl*vx`lh14P%t3!#Ei5f;XMBDVRSuxnQNeRk(f*T> z!QTtShdBo?HaysAq9j>pF+$}+d5IDnZlr)BzDyP@hj2JMR=%T`<&qdW3vK5Ji}g)BuS!>~+$Tb6^#=@UYi&N0J!<9(rjROeFU% z9#0cIKa0|v7F>(dMj-zY6r)#yX!=R39TpoK^bzwqQ6n#WJQYPSlZ?0cb@Is-(VSde zmeH%-?3at0Ena2vlSU?FY64RlIB+WVdfwIV_-6UV?`!iLSYV#S`9G#T`+rP(CYJv; z?WeV*><`#cdtQEA|G^^scEpCKiuo*p_H<4(Ug4-@Wn8S^-*c_;#8h0~Hn1S;@Nzvz z-aLFC9O*&_d051-Or&h3Mz0aS1a3X6ZYIe4G*0+>ynfO*0kXAxym0+`@p^n)MU<%o zG3^K-iVE350is*H@U1W3*-h+GD|RWP8_>KpVgg#eh2PEaZf`Hov-@$IwR`p|g3vY$ z0l(-tJ`5B%5J|=z%YF>_IS!dupZMP|GrH4;P14*Mf1cq$VE9>{SeU^`e|bA;ogTL> zPmlt52@si`<3Nfu;l-jWDUGxO?nrS@b+o}%3G;>f0kW}Bf+IP-)Yy>mXj#?5hp%%J zkSK$y?C<_rBjd$Bh~}QHif-|acWZD5IGEGK+L{v-Nb#1dbWAuy68cw-b`YwD5!v%)M`USr9x5sxklok2FN{>?auxe{b5HDDV9SU z&qmqQzu3ndoZhPAg8}K37dQa7P)yS;+l$w~p3&H6QgPd|b?t+?CWZv)XL?qnO2dda z&02{{yt~V+Coy)uctz>S09VKvOX&{eEnOgJ^AxSxDrRtxpIzmm zZ90x^hvJJ9t34%kax@|9B!r7#5=#h|TF^Kbd^B57J-YO8XIu-nu&X?a%Zj9y#BV~6 z_f5I*jX$R&k)}&A`eYMI(W@*Flp@!iMUud1G}4q@Q4`iOzg*xY|2|xK+fbSCJ&zFU z{IEw5uhAvVLLhUI9U4!udY#o>8(Gq_N^ck+QEymVXT-z-84eZ5^~BQ_80{b?>hr!MGb7 zi=^joUfY;3v^IqaoQH~GY{sEg)wkYk00K=2~HUd zeNY18ttVgziFR{g)5cm4fuq@#6xzf5;W`1E*kO!9=QyoVLM#yA;|8Fau__hH&GM_H z^CPrZz#2M2oepd?S2%RGLR|?3+GzP#62%zpn34H8(hxQN#0yQ1hq58GO_XTL4E01S%Q-J)^BoqC>UJauV{Omd3G$>z}7NbjpBrmAVZ zhAT#L!$LOuq2|Ty-<7gbNr*r08x}C=j}^{liK=qZbqG8 zAzc1t)uve|z5(CX@_A#!L|>lTdPR0*ZlpFp@IJO*obOT(=RbiVsP9q4<+#<~;l2ES zZtRvj3A(5|NYj?Cf*JtaIF-7g(@7nCer?Pn^drG3emk@DAatYaSv1)=Ngp3~&)sid z&t!-1iGrZL%Y?;B*i+L!4qQd}?ju7W0=imE+P@tyGrq>-wnF;mQW;)UfaA^FF8DA~XmTv3ZwcsT=9pKrP? z0~LA6UZ->D7$ETL=382kttaszhq;A#{b&>6_C*m-h6*htqVbGeWskK1nvZ7T!-Uxl z^#c}RbMU9NnZ+I@p5he9Vc%;m|vboHFKM(TZgdqLuMAdOCnygN?RrBl{o@s{PbrKNfls5K%B z)fZBwYJ_WSMti?<@eSJv84*Q8P_a$|n){~mJ5L_ZGVFX9O|5iM8rb2QZnep-fZ>Gi+h7=-6jO#7!Hi zj2N?An$a(wE$UZHO^_j?*0UK&rrikPxDXCh?8Z+uy zD*B>``?VRx1DN`G65)W#mC^v!VmNT@5b{b7poEsmQ}`YdC>A0XY619xVXD)x@G<6W zYvtqkU__enpjv6HTrkmL?#uxpV1mxX(E(Wv#;ITkN73eiyF> zIqu>N19|Sognu~(f3tHB3MM+4>W5bH(g%YHp8Rr^>Q}ITq-fSBK`{edkSVT3Kja3` z4MMg7r85rPFIZaIZwgqMJp$gPI!CNkW?t>%E>Y0!T_Pikf| zG;Lt#B#>|m=|d;~r6F|6oaVrSXVZ}n5H>)R)9w*lN25rBz8cT0M3=)@uJ&HqC*p% z%Lr^HyJ#)}mNRVM7nTNd6TvYfOf96tt!diV_BV<`TsH=ZM$uyE4{BiqR&pS(e=0RC z01no)U1wf#hz*A}kpp3()pt0YPwM0Qhkq!0n+|z56bK~|+7bdtQ5Y{&tk#%IYIxF4 z*5S!UxERI^SQFaw|D{hWIP8FQP=(16Y*gZL!S-srC78@LRHA2XMBZZjr}dV_&Z4fo z8BN*NH%Ji|>mayl+MLQ%qdcU}QG_KGh)LWuj46<9?qtuQnh69^vs{P4%(_v3q-q!w z7B1mFbPoabFfVw_n1np*;*!YBfz=)+Y#l+z1R;~R3 znzo0>SdR&fo(fkQNunc<%7oWd4Vumxi&;suf`ywQ!KPW8Og8F;wgXDqqCj{JCo*QJ z6E=rqlc3h1Ki8LJZ5G+$r*@ilirdf7X3O%umbpU{r-&l9QRvX z`+E2JoW8bkfBpM8eg;vQ%I4GBxn{EEKDVAjLf8Rn16j`l!XUG`Sa#8?cP(#V^USac zcz^DELdXIbyj=V~p|I!JbrCRd7nt?)d3)^Tmw8n>X~y~{nxLQ#dp7+wyh-X(+aaXb5x^^`|X@Am!_g;4wK_PlyEtZ-55o>t3|#(-zq zL9hzY63_uWEVxqu2Fw^%c5nRFs(U}r&j%|jr_TU>ot+jXF*N0X*u}I2C!BHR%{k*v zQE554G+hy$;?tJ`UIUs@zd3X22#u(#arDO(6CCltWn-Rpk|Tg;hG0EAZbf?26In%Ub%`Mof4z1L7HW(S zq$U9NCE(a#2l1Mb646-#6Qs4^FsVY$>ZsA0PC^(vB>XW?VLITO;?aY)QVCWQs49Jk zIUt%nv56?<9KlHXB?)g!%5W=1zzWqDRcpz_2+Aa?4x3KV`i0wA+O~ZgjRhU9n&0J8G6rzt?6m`goowwp~>ZMzKpV*yzW9h8k!!?T#QE}P)^=i}64yp5`8v;ZI zOT&?OMKV4GypW)~yqwd*hOJ8!M6Dc50ovV57GA=r7sHXA6^$DSHh=y5MJ|-84=Uj3 zoDWTgNz?K#j-&6H@L0+vyt%(Us~>Q$cYiw{hJTUTgq}EK-leCAOvW`8$mKK1%GPKL zI_WBmtgP@}fkfy;b))w@md;Rb9U3n?@r)A@=C4V-hm~AV&qK3kMDn6U1s8>YWoR8W^o9+;b$FO=>CoIh)A0_*_32|by&*7E zDBYFA(#~nOM%gUdhz?=fEV^95uOc}SuY;&_%A;JUjO^^^OFP)%q8(%0dZ*O+ThrZCA+P~fdtd-p&e6bG{ zDSd0QXPQ9$!_=;C9Z)<`e)-bLy4sehe7rv3XDOEpIx(zie%aBib~E-SSqa}IJ<*oF zziIjMX-f9$-0pq9{lmANQ@iW;@8s556OFXGJ}| zPi|izl)YC{Xk`5W{mkDglcMrkFzn;tG~SbeITYGcX3L61mqem#RmsR{|Y1$Z< zpGJsesqQYwyT1%tG&-GL4h^?6*K^C4qZ?k_%D0fj=`0vHd1yxyzNYh9{Awm%?036* zecoY$6rSIwkA~kiV_sihUknH!KkgpE#E4@R6tKS^Emyx*uU_2%PxqDf?qv#w4@Z7a z%Z;DMT~BsT2X6p?!VCw;o21Q+*A7CzC1y5vHpT&l+|39{b>)#tSS1}WBQU_zf; zAXg%LaxdGVrqSzPzQl-^M@W+^Aw~72cjQ6)9`Dq|vc8>zNqgjl1tG zQ@)+p^ctwx{I`vPL3dLvA!CmZsFEMsb8jPKFz6Oh>=ZyOu3OHp3G(>}7`Dy-o)$I$ z-f8rcl02SKJL=`=B5UFf!LMX$WO%J zrUrzv<3oTngF9}-Y2B<@tTF0|2%>*8fj=$oY&fgAZ*=p(E1+8JHLgz z9q$-kQ6N5i5O{>40j6x2zcQvQBnam%@tc^!U4bu^s?TxBJ6z9qj>JCp1#b(wVHgn% z5D%;p(3&H9$RPEoi38IpXP-xJ_@%13?@*c#s?20u1PEaOKs6*?W; zZ$k(KJy(?rDviPh6cM@zEXkS;FId>`BKwQs?eFQ_8V_UXb=sS=bY7jHK~#P1Y=`Ex z3D4t4{`#7H*r}umWZ`HJFSk52{(+Fq0SKvBqZ0zYj0xrZAg@1tzX@+J{Exsv1n7Yl zP`k*MYNB5RDfKQ0uRAjM+Gl2&3JqD$915m17E{aVzR?7kpjap+kTq5zP?g!w;L$7n zibslJjdcQ=VaIc$dK+49sPnYIV{k>0QmXXm~HRU7-lb&odFau5U~`TBKN3ia5h%-~@e09szyNw=MlP z=g0)j{A}VXAYG{Qy}5D0I{lhSC3!swY0j1KI%F zGuwEZFj|IJk4@QdDJs2!>O|5asLl&ibN zkfaxC@TD<0Knsw-Kgp09*slm=z|AfFx3QGAcQnt;7epUg!YYR0>Xb;;0i;p72cn}j z&=R!&cks1xnyjVE&71)Y))z#;1{}r-Zbnf&QFYZD7uAj$-x?oYwh)D^#$r6hEn0)a zl0m)Vn^ir!>byMF%gQxG?v;b!=tr&URn&gOnqrde#+hP zQM|wJA@c+OgQgP?8JVs}{xcVZUI4>qfy58|#tZndnK}uAVc1Rse9DR9U_^}JJWu1> zqJ6S&iD&LXOvpwu=`}H|=L#I+HG_UyrWe7W_=;*Yim6Agoa+bFN8)`*s9o-X-{K>(~YRQg^dIKX*OBI!fqlJ_mzXqr?x#59$&1Xr#FpifPT%MN6oY=a5ctGcZF{PaQ)u|^0I zPuVxVUXZ9q-ZjLF{CO6?n1*Z;0MJ36MA2rPp2jjETlW7wNBpCYR%)_OAzPr6wC|}R zl$$rts(kRS0)o4XLMDCGQ5x&FiZl*@8ks!ua+A+xNIOhVs2M(=w#!B*8#B#3CI}#E z)v=c+U5_r4s9v!WiLB&qt^Ks(>LSO-_dZIyAYhKv?W^r5Vt2X-eGX6|fM1TZzeRPE zCsZ*I4&CXyo)>xrghyQ`L~QY1;McV(Q;j5+Sp9MJ@1_2kXW5A6ufG;7e*&3OOVlHW zH5-;Lt?>|31{-Wq6c(y%^X+RwBAZtoKr1nRjjY|T0p+j&^~YN)1!|75&6$?egk}_O z10Y;$2&^KWGn~KC;~$#~JIwkdvT(LN*z9>#w^8kN>ldV}HApLTK(a?%iraax*_CtD zQHN91&v^p}o0X3qJDH4`I$=>)zPmS#j3ye*$zI3LHdVU=&2v~do8nN-di?8}C0BAu zP=<2_lTS~1fh{~Mhr#Uv2#%i{i$PmQ=quJ4_DjC+4kLEe1J+H+VZd&RS55drJEZxb zk&!Xz>;@40u<9ast5}6=pZ$-Baz5Q&Ci`b4#eJeze`aZJ2uv$$kN7hG{s|N`mt|XH zGWt^x`?|n<%DYnQ{05A&j0mYv0m11Z&DPbgG7;q#Q)sZk`5WWk^rEU@bSWCY)}Y|4fl?5OmWIZX0{t>@DEa(!bjR;6Bq?iZQstK9X< zdY__=J45To=w<9B-z&xi|NHlE4s4fe$Jm%9RcG{U{fbxFc=}WyZU~tZc3_-}lthIX z+MiCt^peGpFz8jF1rBdN{^>a66;dE zMk1&9==?q0C*Hzv+rV(6=9_*(aZo^M=jO2-s!&gm$hSiCjMo$Ze*M|;OnDQ0tYY8a zhOhWbkd~{c|mWQyYQiytm}=O2&rTP3-fCK3J7%94YW^vl|=@_lD-=n{>hgOZS|+AgOS zFRNPRr)AAcg0B`0ZYxhVCU8=9l5tj{6o9X!u~%lMDvBCg2>CI|5r&H+GRww!)GH%K?4aou zvNXBsggP^5_LU`#mL3|zavE-!O!Y&Ur8MN7jB*G%GZlqowLNi&A~nM}WRdAYB4A!# zRvYbVsMvuPZ^d2iyyRZ_0iongnQG;eBuka$W0HLOOH_)X8U#VwkBZx-Gt(G4BZC^(`%^;1)AgXr`IAV_c@ndDWdh&TCA}hsU$ykCSb#38_ z8chZ(4u!16g0hkXxh=V5#9}4B7(j8NR$7o3i#}zZB))_cN{XI}QR(K4H@`pRnH&0Re#f{1iAo-q0FVGz55T(>l4na_ubx zmrnIKd`vZ_3!V87`>-&K2i3vk_-wWoZVx)_Y%rmztSk(~##aYqL-Qcim0Q6|%n*<% zn`3d0>P>O4EWfI7a6li0RP)}v&?$MFJqehpa+PctzJ3%0y4D;-Gx)xma5aOe6)+Lb zqJ9$uJ13f$^`3R|-Gq945{#AM+&Uc^tD-E-9}7cThre3&hT`+l+6E6xBUx~ok7B;# z#AQXmV(hm{*4Vbj4ixnc>rkQkw0h#M9-+fgbuijUaCL4Ot^iu^up*Nr_Id@b5nCFk zXXE<9gN(Ry8PZ5-xH|HmhkT$X(Dj&&*-;zO@WO18TDgw5%vlX(G1EaM!B}8f6Pfy> z%IVbK-*>;S1$w{t-}3-(i@jer3IgAsc>*6todmrf-c|A{RM!qPBhXzSNdj3`S;(TYb z^IJ?z=d3QslBk{<3|Yz}hQY5@hV>=*#!RVqT`b z?Dwh8;)DHnm&8X!O3&P%))9%!y;25oq{8+MC}Er`zg>e%smaKjyRDG5WE7AI21 zxi(P{k(i4z(T;4MS1AmFiX6b}+Z2x|106l2l7#3{Q2u#Csu6I)8sOhJi@RmX{SFOV} zu*I425|5BvynGmzI#yuLBR+<^Z|gI~vegp^IK3?p-8`0i0|?wf5$>|qReL==hwqBm>*Y8KwvyZIoKm%x^0zF5+{W4TlU9AQ8gIKNK+I-Ut*{w2*OZa--SxLdrzY zL^kA7NWrJJ8zf^YglRz9Dk@mOP#?;{0D?UkBp*SBqg}zqt@R80F(D0rkRQ5ot<=#0 z-=G}Z&P`t91Dj6k#?wmmq)OK&j6!f$;T+Khs@xhB`<3}6b4{G97*`W(i1Xe#)tsPz z0M?=B@~b${+655Ih%W?d$Rpv;7aPR%+}$~m$7u6BuS8HKw$9f zRXQnPqQFh2>BHfKl*Cotsf;hp_v8fV(Ch>W#7;>95fs|QKY^~77)ph}@(cnZGMK4P zGJz2SlNxy#s6V0WDULjGE|C;05$siuM}sp0@exg)1k5*QfiIAkyDhqiCi+&yhb<6c ze;JBTRO7+kaNTw%RbJ!lq2(DHu#MvJS5C(jr)eI_yN+C9E{N4Lh!hg)N7z;TXO}5`;d*;bBT`*5pcW)^n+uLenE$pM;C=gWDy-8E zglP@NHzFSsKe|?dbc@O33d&pNcw43KBEm8&-hh{QwwR-Wd};*F}2{)K*FMSh+ogTGc?H$;I0H^}Px9rh|@0dj<1 zMV6QT5n=lY+e)sZ_JnF2;Q4frc4s)HY$E3)?AkJH77pbbN-BmN5d7?SdOErR7cvnJ zcAWo7kYs=#m?i@SmLNQv^6*+R|YR#NDX?mGEv z>Ha*bLO(O9`(a}Z;HF|eUg1mx`r;Ddy-F8-!JjxKqQe_fOh*8Eol0BGChBNNc9Zbu zCHL-P6Rl#s6RbwmFF|+@HeN?k*wzQ@G!+f8Q zbe|5)3W87K=?=k(&o)jS!3U!;Ua6%%;H3P|s_zwkuvt+gfC#_BQX9tyo8&oBK z$OoSC%%w&gB)Y#Vh%TlvwRm~w(#G1_GO`Ldii<7TC3c64?SFd8f2i($TWEVt%9nKa zz-CXz0cS*jWHT?!#UE^O2#tHBy!gk)5_xv zV#E-xC>$W9Swu;E>zsL}uDnr_!HhpVaelg^lR01k3)F=j0C{=t2L%hJle6>l2CLy| zf!ol5A-9fce*&lk`OT5GInel0WPh&eBtTlp#6LG#^)%3~5~}P30hOPl0776o?ajBo zA_UK)p!ta7Lj~T>!dJU8k5thIs>02_=6}`p0JF#HY9AWKbWjr5Ak!TD+o90^RNk_Ca)yd8>@ooR`_#I%zbMCBRzZ(k+3@)s z3tm^sJwSvXnw2!n0`YFSi;WsO$oJ^(h9mosZ*yiLh8LkT?ExKdD3|`T2kGNcsPj6R z0AV;m!f?DR zK@`w!t50O z=PI+;5Ebw&arET)Po5NzcfxH>i1bt3&d+81XP5N;=S-oFu2j1}A2{P6S8gF2@ZL_1 zl3e|ZxkFK%o)F|Aqu{LqZMs!(Ss~A2=6y@NP?$jZ*yYN&{h5l(9p-rEzG3t;WAl`` z^|`dv5n@NhBJ(COw?no;#80}wHA6VhR_=FHRRGXKwIbjD$U=TVD8k+duJ?7cd|x;;8hk^AQ2HT$L)boE73wAf@QQP|0p-A|%Y)ZcQag4bS6k`V8RZ zS9f`P289T>I$->D_DIkEJy!()w;I~#rJcD)u{IOxZ2Pb`ztj`ZPNx-nMTS9n@8&XL zo<;Wm$JIB+XcBhYHm9v=+qP}nwl!_@ZBN^_ZQHhO+vc6`o|Bt%?vH2ZNhOu4q^got zYwxv|snb&gu zRk~}39?h|lroxBo#F?EE1l0C(mH}1xcLGES0tLwD+LO89W=BF{LojcD0+4PYL|{8G zGcMQexm~O(3V)7ik4=xUNJ;@Xx5-qH#Zgro#R5kA2sq1xi%g_k|@3LnV*@DaGC8DZ0Vf24?_+XqntA_A9u1tZ_Yrs{>wt~%^e1`i~3|fOyrLMDS(aw2mJ~K zh1deI)eZu9f2`c7d0Fu18^hWETB|AOPzL5}*)XqV5wFeOUxeEs+9Z7@*kbYTS~1+z z1b)_KXX%I@)J$n^+$b-g_4CZC%R&`k-su3xc<9mxD#Ym_Dz{5OmwlJ~eZoh2tfxFb z|K@6$Z|n@fBc%9ehOgY#w;!RT*)PNk#uCztUga1#gkJHVlJTFC)(*?=!c!XAj2Pev z(?ZYpv`rh7JFetX!Vm-WCgbDdb5#js^t4R>n7`eAq-^c(`4|b_b3@>8#&}82tW(2I zvzhE}Ka%ZXzfOMVIxs+f^PjTQJUCFi3#ecax=L{B*xO5R>^K0v+`vUN*h%)YAJG%L zWZW2cadlE-QQ3Saef7RbE~^bUZ7C!sBi5ERLfCF64GPz82~u>pNN&Arj*8wSdvg_4 zeHZ-u7zi)fWA&8E@*MG`8}ACwU922(qz4DXktCl#E>ToO{Mmhys>}z4WhWxBM$d zGG#szXOOW063zwb?JF&|l!yghEq)mI8wRHz!D)rmmGd=;k~=amHNa=9Cv%npGj2RU zygRpkGo3-i__}wY&SIgjzr+E*9=!vJK|Q>z;g_?RQJ8BZ5Is5b1mgvTnX2*M0D>9GKV@Y%K86 z3%DqdLzghIy=qUeb^;;KzyctTu0BP-PakFtzfQV43jH{{0$QLnJHUh(@Z-yt0r2DA zh6lP^;RR&uMVqV+s^5`z4HZzw4LCIr48VR5GveJ>lGtZG$yZ$Ro!WLxn!{WD?w|$m zb+-ZdyxPIzzLU-@be7njQjk`r^;9)--j14I*fkdHYGG34(gdO+m0uzlTcJJBZBbo` z;(vcE8UFS&c}k`616a9)K!}tx^W_R|?LELk7OmnB7RkK=WpnifR<$ld;r(eWTGkhb z_1po8|NbpfO)vOSbEF&mJRvPzS_6<&ZJZJNlFFiCtFvQ$T*PXC!VGGp#2Y0(yf}WY zCK)q|_^@ShCNGA&hp}`L5XfMI0Bd|}2kyR*t6%-qgUvhe1dtKGu8PM)*2io@<{1@# zT%AqI8Av+-BB+@)&Zn-2;U#ll{Yu*v{FNvE6^i(|oh`K^Y`r6V@f7&y8#JnCbHcna zXKbnq)fd?Lp1Z&^q$SrA!p;XHnWJ*ljb-YVXx%aZouL>?*F+$l?R1=Z{kJ$aXWTrC zCK0nz`mUgFA%LYXkVeuHbcUg9&`=OBk0tM*37I1lRd$y^MTLKV)T^baY@u@J*Ss&b zgrzVK%#Zxe*UQY#&(2QIAPg|k4018#=aX*Y%Yhnd-&<3kLxanP_Re9kV2+foweK?F z?yp#_NOsmQ(Ze_W-{|dDYrs^JhD?8mRhLmq)dtMRcV()JuO-BL&iW(HN zM#F&iyQq;qO6r}@;?8B_TCkFf$c&w*<#yxtNdTS2Wk4pKgu$5ZtN_ra6+BpPRkHk= z)4S_k{R#8>1Ll>RZR4^{&4chme6p{o&PLG3*m&}>r{5x5X-wu}@LNKhtQj>>vutQi zqqRr1TMe80!|qaeB>9n6Mpdf%TWYN%+3{`oOaUs~vN0YP+=C1BZK?U_$NU+a0OW@-s?8tYH3?IAJA@2PxZQ%MGY8$M12WR>$^#WK}iu>ZeHXFYul97eQ z1$o^!i<%tj908K$AA-g1eCQ4E=AXh}FY?X;=Q;2Cu&Ea3@7LyyQRC96fw|a)pZh~v zz$Z|V`)}pz@fS$>{&2MY?0!qOes}W&UM85+nl)PfZr7&lL%D5VLjm zwZk^ZYF^fciziw(2ZS=sJcYtd=)|nnUUz!Zak?-PoKO9L3bxzW{(qcsf&md2C>sk$3St=;5f~#U zBXdfh7BDt|iH+fZNy4)qeM1Qy#n!Q_V+(t z@GdnqBvOwl7`41918JNOBxQg0rM2oMX{z=BRno5HbIIwq1C=!SS+}Cvu@({7Q1sHE z*kB||$HgHzoLs?8=UD+pS5yHRtbhcN7Shkhpqx1_Q|YlJpm_QsrJR#als^94URAA! z0M-;aLt3d4@3OWFY9Ku&BGlZ_A!{uw7Xdo86e>}4R+_N)c_vpVLNs^9CK4qLiQA|12nP6-1M93A1HV4_{b)^HWOZL2|;#|bOOaM@w*!k~O z^d@Nx3K5=@ARi>bYWjF9vKIdU>Rqf-1;@HyhdKk@mAXU%qQ_XJN)&O(6h&bDRKb?x zkWDIJLVn^@$YAt>edxfykv^43q(l(gCsE%`;dP;%w7lX3lFWJ)j43&vsr{%;24g|M zjTpiBn8%I4%t{e8XknD8qXD<2ef7+T;Udx#Ukz)77%La>l3|>5kpxW*ON9kZCG&R$ z>JT+6?DZuCf!Z`R=EET!p+G>akrYbHZvH|bny#2AnCS${Mh&wXRsw#noWe!$DTaa5 z61a|!#|U|^gG+$ZpgdDx6FH~{D*OA+E~nC#M@@b~R{rXQ4k6R%4RMiflSWfe>YF6U z+=GBM)&#%*lO~7xh>ewmvJBm^iYW7G8iNiz1j72ybk+gePlHzFFNemGoI_Qha9%0_ z%`DUui2b<%G|}~F1R=ouurFDvxP>5=Lwr_I36kG)E*F)$Hk2P03{t~>F~R|>c56}* zd_8w=&P@&*&I0?rH(d!DTDt&628;&*3$6&|8B7Eh$;pf|)=j=r8k*!ph9GFNzY_AV zlb`#+Z|$tFBT_{~>CN%5a1%#05O9Hc2DT+BPSCzf>5>3Pf_K2D&;!Sbx|swnum6=o z5zysl-lY**#=@F_Nfx-@RUyzCBkLxl<`~#<@Q}Y>eWNr2HjY5`ZgstbMBWJK5~xKc zHV+h*Ir1@0bG|_)JQ#Bg3yKd5VPWc1rGq=T>1M#6!c*FHhCt2w%g$loFdM{Eenpt7 zTB=!x+#q4NZB~Gs@t=X{@N}EfZZ%FP&QQNNWh~6TTvI;=g$xy}Agw+PXb`1rTTDi4 zEw71z#Qut0-78RZMq9Nue5}fujomnik^qX%80|zwCzW7X)<=fVsTkv+z_wB=o1HRn1 zh|S2;ef17x$kX`KJ?n*I{Wbe?@VZH3zRrrnR;%qnyVF+%qa}9vijpokaBIkNDU+D8 zzl#_6WhDT1(mU{=2^BjBii>U>X;PD@VO4y>gh`)_U50GxF=PxUwW}^$tTl#i_MJl3 z2dyHBe9sO#G#U-eRu-h6s;<@9S~|D{o2=l-ZY+Vobs&G*Kr*Iz1$!d0S^q?mebRH_ zGW7~B^$DwC%Xk^isiSU_aRe?w(&8*+%-7~kHWly{iJ3b+<&o@nU%_!vpqakOfH_{A zGTAPM{EW7v(`njz8yrLTPrKLm%2JMeL@ZEyxzku87G zAOh&I*VnvWx+vRbfJt*4s5CjpuNeP#*{zX=@dL$|&-7b@?RXm87;IzOO>t57k(z{} zjDwZ2db+foc$yB!P4lsOTTXrQ3HFJnkTueykTH{^8uAH;0vw^UFOHtRPDz0_eGRgP z0jq!aL`|3WRURh3jLk5Dak5--jpo!-1Gsk@YBKGoQg^V8iKcWMxU_nTNxDRSSVs9Y zX>Jnm?L>a%%3jk$DeYzW3K$RpR&Ql9JswRdGz+9I@6gCeU-> zn%^&-JS$ILr7$(=3;o+=tdC^;82k*)K$)NB2N-+)-YK<~nO8>q=2F(LwJGV&I^1$l ze+=_2gjnhF`ugYTi||fcWl2O2m{WTQ>)eIsO|WZimikZ1;YvwqOqkhddR_8S zy08AA8;rPGMA%0?ZQQPh>6_r?E_m!2>^^iJ4w1gFW@sc+ikuTHsW(YiVX34==e|cxI3V_~gvQ%g;9M z2F<)QD{rI!3v%pge(-{X$=f%<2-E!YC$j4+Sc`otc(i{G3<;6_EJ6RlVZ<8bY& z)W2=2Do5_#b@m1C&O~qnI7H^8j+$pp7r>=Tx@**(}?%9SG-sBs_P7hY9(-fVDVD%($le1KL40S>VVY zV;}IkH0Q=T6%x63)P~lhY6~|&L$;1EV2dK&?KUiaH$YQ)QcG1%J1+9cKov36xwN4! zXPY565A!r4X|EOoNIB5?GMtS*j|ntViLowj@p>BJ^v?Tcp#1d&;?UD>&ns;AF|o}N zd?%)umdh>kwl}O+Q9VSqi{Ml`Y(Kd2MSwZCr<6=rQyzn!{)+Q8*71;Q)$Y}+o68^B zht!qq;@GwKA!)vA&C6l#^tLB9J~$&ZBgomt2L)ei`G?T~Kz0msoz-zMWPzvdlaV%Z zas9;C&M*^$PyfK0gD|?qeAw?9KCp4~`AieAD?C$sUKaTVK zcUqH<`7wh04G|l5D*EwVY`Q1C$Mx6V5nt+ww&hPP{v>ge^37d+wr5Pn+4je)&-RIg z(~VP_SB{PR&9*!HfF=9Kb=)08?bbj(yfFRD(+W$~)gKRlFxHWSj@;eeXl>f(9VO5B z%D<0|q1E+SygQ}qJ1*vLpzOYK;q|>&-^l4J(GMVXJ!?Kekq8BZneo3-CI<)8f1^xB zMvnifhYQw_Za$)b&wQck1i^X)ZJsN5&)gQw%3k`W4#-FwwwFEG<(sb$f(+(sb?doT zbsQ|}@413L*ztYp-GRDpp zb8ngmzk{QpxfRn<17L6ab!}4IPS6S~(2qo;k`YmZ4b>=|U_XWXH4e;e-w8aH_XqbW zpmgTsiQ~Gj5Jc%ziy=h3E@|!h>vS1#t7guJ#I<@-=X5#t#b7Bia(!^}Edfd7Wmthz z$ZQax?9{PVPx!~UI~LL0g%5_eA5odsla|A)&)S& z-DW3mCdK6LM!gOYQtTZZxu4us$ z*KXGuqpY6V^J3}UFh&~oz&C=@N&M>d=Oytfsx(|eBsZYr;4ldYM(B!0l|Tw+A);ie z3{-&RFrpRe1vp^LO^OZ$=$Q_xB$9}Jiku}1Oqt*yF%d1JvQ;6&d$Iz7rZWLK{Z#~6 z#zo_E7u5cx2j3GMdvCAATt$?wglYfRvdq8~0jB_ISfLujO59dL812abi@`0x);W*S zeNLPLmF}4@a;zvVoAKculJwHTjAAQ}vnbXP6{fZT0)QUr0`v=2tiR6^)g3JW3^|^m zQ5U*l#U1A7wbVE`6aHEvp3!Hq_tg(t0#bt-9`u~B$)4x%&jGO+y+OqIP%c7fZX`V> zRKoojYLwLZLC-ilv~g+DoDfT`5M>{9$)B|D{ib4JU|W`4)KNv7tg?Ws#sra3pjIhl z=pp#ugaD5RQ_MMd0jsq%h1Ef(Fe-wiI*Hgpws5cjtKdZlSvuswdjD89Ake>YCgJ3W zlyc(&SO)D_v8}ccrQ{aEFSU?=?Grx=+{iSO|2?E}A1hUT5a2nlCy5SMCVhzDXyCYCp z!~u6$K6}c2y@>dzGz0MfDh!MR(1ys~kKV`;H=|`mhRoQf@ok~e%o0)))67yg&;D7n zWUp+>`UP?`Iom?T$bB}cih2oP(31ewMao1WIVM<;3;CZ#D>lE0!0(zNW#vwdaec;nKPtR&bw(gI zg62K7=D&{l7XayduqBR9OLnB}Jb2Q%dUOQaLtx{?IjSw`J0Y0(FFx(`{3@Gfa2PS` zknd|5k%6luRNY0mkNPQh+xwgxKxDJlWg&#`tu8K^?z-YQS)sdSOhuW&Rn3On&|Rv% zmqS1LBY@W1veC{z)JjADC zpWl8lOYkJuxhmTZ3hQ1rluURWb+K$t2Db0IsPH`VRU2u$N@=f(H7=BQP=ZLi*>XV8 zSyU{rJBVL_M^XlTGs02bf0k8$G6*}bSx(Zqx><(t`si@kJWz^IzX-@3>f<15UwZ;q z+q_T6@{ahH7zk!;eJgwiR6IT?HLIv+UiwjApNwr+RTMa_R=7-*3iP~&3-yrCBe_mI z%sEpcwf4$A*=^w1(0wuKEeoQv?<|g2*y#Ah4^=ep!y~yx*Ax)YsVnR5G&;a#=5R2dP7#8JXs)M5PYc0x;t1=#zoKZ z^2@Bj5-?-t!L4-*`!Qk{oRsILuk<+ zfo0RAH?8{0J}is|FqST@y#Gt8NjD3l?-IPQ4u*X^e6u3BSn|t3hMz@+-MQN0nU(t6 zP{)arxe&QCF!Jm9&^0~~otzV713pJcsw`&DqbMoM_5i#)C81uHc{BPW-oE2Ajj`kN zEy+5CKVoh1oljltG4kO%W=2H+@#*LFRQ0Z8ZT)|Ut z!AoYVGxqJiSR`X}u5sG{QMvDE4Vunzd)`pW>h4JU6a8|QQFzAnYD^WnV#f{NTT)}z zZ4}a{hw#E<^83m;gQ>m<@33${@|(iHLq|L3sbu=?U;&wK&oy2>r*6YGUGMl{WIi>^ zxU|bkn*JrrQdupBzw=TtOM3vP8NWBT+1i6O`i{cq3-NO;eMO_KMUOv%h5L`rJ#X3% zC|Fb5c3jAM+`o`_bD)M$;&`&2IC$PFb zR;6DTcW#R`KH)~@H`9LU-@*?=M+pJT|uk?IGJCJkY`lUJ=*! zH2`%b;<@OJ^Lt+&W3t~v-_>L5e49%M%w!Uucx^4%N^v|l8;_BCU3P6THW*u)0Hl?m z+f>c8yUOL&KEhEU^STzMC6QtTF9XIGvn+n zUwl?O0jo=h@~&^`BxHZxdLIfVh_A3;5x+CXRjz2k9Qf=SRa4EYrHd7UY?L<85Fnia z8kH-9VWF~4uH@Okj;Z+9Za%C>@N{u0I?3-+u2W@>xk$}ph51O)%VAcvx`OCQ{B3ab z61Ox5d*%+1^D(3I5g-5g-)hm|T^ST~DCRs;piL<{pll_+_F(tvVWFPF;7~oiMSUGz zj$xeExYEpXQL_0n;Sj)LQ*;pj=Bp~-a?Z%*N8qPz7}$5G;( z>j7Kux<{(YzfCM~%O5ltfzj0x4rUG3OdkOv$2LN|>}nX&3z+f#@JDQ=P#bB?4uRHq z4FPXU=Dl#;NocQv_>ILpHGrQh`Q<@s z&dOd&eiQLS{ZV@XL9lESj5m@OHH~!Q2p`kvO^UYBX?4ei<^(v1l-!a`PhY`nF@IWSOT?i)Y1QHczw36K~rO-8&M!jGL(I<0$WTQ>fi3 z#m^&k&CelBxc%j8gw5Ll1C6KUci{HAvj>i&vY!L@ z!YdCdefyT7M*s{*J)4`orc94t#Z^%OdQLR!#tKXEmu|&(kU8y>3`|6ZJ&uw?|5vqh zf@=E>0_YC@uR2~!cORd4hbfv?iNs0u)Rw|4we!{4Jm1UHJujZe4ZuF?fO1c&LUsK;XrwWVum8YJ zo&zWc>BFA!Up?sK^30SqVV_u*xxaos%uy1hP?^AA_&l&C3{3k}9QK^5Js zibEV{-Ep?`2aXF{qSmHAUh^JntCK3_7qwpwyf($w5Y-L~4XoXh5&SbqO6xBjbw-Eh z*fV3XqAv*77I>mh7gsHsy4TYe9B-KqUUd(sC4go5)^YV~+cq5~y4Hi!%bhj|vEsX| zj$7yzwoRwb)9-7y*b19njuiPLzHDWd4JBLr&8np*xbuXLfx8|2fDl^U*qG>=Xi3fC zTbN@ED9;|AEW8<7iH+x{Bue8< z2SEM#ee;W;8s5g&N7o|5!Nl=W@1-ePB|E;ZJxOIj@u;l(Bl_#JyB6NMWs98fP?3=kZf-H#L9yn>L z%FePO{z2^zb4%S)|4xzJz#D96+j+0e<5c`;*V%B(dgKZZa-v*n^32FmG77#* zpOUjU18hoL0tV)ph7Be)Cph`g#FP23lANx;92YKQY-j#1HNb){&-T3ts1=#NOcl}R zR%O>8!95=_1Xx-nzmx{N@78}foFj#_2@7JC8i8$J00@eHMiYj!BKBEoHf~dP+DOL@ zL`13z*;OMCjs^=3cL3F5i-od3w$ef~wmp+UQgak4fj|j~Y)p`^K~ZDeyqT&1Np?*_ z;}7RUg`&Q<6yTqQyn?DxR)mpmS-6e_4Y^37FIcI~8YRPZ>>gfwp!$=jyGjgG)$ckS zQAvux1SQPqL#hoGKwJ6g05dse3DGDMK9yTN7L_L|i?X1JC>#}6`5u*j$Cuos(wmP#>Ss~_q$aEgKr!Y&NL5cX_R7Dc$zva7%3IwEQMX11t5$Y_$+7(4>>(w-!*_hJ#NvQcRB2zlODj=(+U5# z0_L~uwJaEeWywI#DfHRT$hC`nb1r(uAOMhyyg;A7cg2m=x=Pt%ckG(ia_r^(0J>Om z1->Eu;!`v1&AJ^MO}>rrC!eSDqLx^TyR^*sm&c2fpOILmMw@!#)SWA=*Nx(L9%^60y^1l$~@ zWqlbf`pghsKcq0-DfjOkA9knE96svIrRmv{|88`(Vc+rc zB1?#8$sXf;{vC73a}RsN?g$z@4N%MZ;$wilmVD{LN|Qwn*j|;+5x4~X;P%~FRbHEr z`p@R<259RZ9GozQ`K0Whm{@=g=8PvXyC|hCW{cjz+-c*X|8abz-+8;>882qu$+UPx zh)X`J*lTk?`?;PeI$S5*tvLWStIzX(#`q^G1-54Mo*AV#-=qbR^-jCF z@cYLWv%>HrHAtT(hVNNTZqF31+97y^ttq^uXoubJV9P+u2TB9Dwf_cLEb&tK3;*xm8#8w87t!vXBk-+a(`02J^ zML6CPj~U`gzdmN>RqK0Fbd3;y2u81>odoE+Uy{Gr9w%gJq~X^^cDJyPw*3>m^jIo3 z?HHz*+!$yq>G9f z^P}Zt$t946TORa3)-m%i+#b4-x%)n6>0f@at=(D`Ypm+S*8fryr&0kF;YqH?)>@V= z4qPw%&O;!Ab^k>iu>23=;0_WD2f)C_^gmk`L)y|Y+s&w3FX|R+wkG%Y_uyc^!hc;A zQv|R@Gz+3XPqGX65&SYLIi0y_L!pdI6d6M<(RB|Y?!JE5{A*&pSV%Tw$mAI=B|p7e z!y(G;Q9&k)*Yi|pGib+rR(h8~PDIn-N<@9}DbgpOo;5(J2>9j8RG)!64ac#%5yraL zl97rz0-lZm5V;|*=;YZQ!`j4NH0g?maCL;%4wwzfz*OfjHyxIxkqx3_xc83!UW zZUj~xHrx#|19*T~)#7-^%^t8+lTbT@!GhSvtA?;7AB5GBOw^5l1&TVRn5~ijVScB=6cMOV0yc9D2`Vk#*H8`!$b6)DQNK8CM0$ zP=+Fg+5fKdo7=$XQdgcYVuP>Pu=`}9hyfHhbS5~JL&bt9ph3hKIi<}SH+3xI zjtGIx2EcOk+2w;@hIAUiPryNfD0(`uIC0m}RMMrodXLUqDj z0)JO?mIcQa3XN^%m4CaGAa`QqBbHw6?>5_tHr-6|u_c==1y zx_REYuo-oqi8O~f(?eo`5e*3p4U1X`f*_Fwp_qe)ju}$Qv_3ADNE|IF3#uK>uLCIT zGZM)a5!|T{L)G0$G0ql}4i}UYRA?rp-ETpdFpBXxzPLd*)ED%3KELpPf|SfTqs=#; z4rcg2Z){;||Rda2fYs~r9;?_HT z$Fi?o*JX(>3O4puH(ZUzoAI`94X(A>WOMIp&a67Qwx3p^W!aS!q}1u9 z7lB_kk@Hq5Natyj2K=o3Yd7q+&jnvp(KqB++cR^~H|oC6+62&sDU1Vm`@5gHA&*Fo zqc2-Qd*}p!Zu|&_E@jK`8;|Kg5DLFass_h?H zJ%o;5ccJicq$R~x$4|~>K3)g8=g(hfY}4%^A#@nUC={7F@EF-&KnV_`1PBr#9phWh znRV4u@4L3xA#TR&>koz4l+n-|M?PhsS>^s3VR=b?N2%LN696A0=yi}Bz36a2w68tq z5aCH`bKnFf+JPKB`J!S7M2a={AN^?oW=d1>FSzxJ62CTK+BN}3eUOE8z- z@%*bLCd^QFO4GzP(vY*r9XmhocbBsm_4}JtdKwv87+|k$fSE)dMm8q6m}HD8WrbWT zm^|skyIA|gobW;C6}@{hl$?3rvJiap7a^UBDJ`UEaJ_I@hSg`MLRRZUVtYyUyihUX z+sF;z{nawzF=xkTljQ68UkvOo(Bl0;*7sTROa<$$&-khrU2A+vblZc`71zWk1s=jL zzC*JFZh*^j4sf}GHC<`Z%IkjiyO!ONM?D^%qMUf5d4!?W>n;pmIQpr~-!hui86M|{ zAf}vWp66uwA>#W6)0t3q=KU#_&NY~Ajt8}~BKH?2ytzJNE-|GmhPe!QfRcXJZGoF2-6)Etf~8)K@TWopH-#ZZc?VWFMQ(1Jth-cK&e0Sx}>i*fc9JFpmYHbz4_>p=@<8 zx&s<2^J=GxxHm)Ajxq}x6s;(UKXUGM*XqyaJVm%3ilQX$dG1CgxO}I*m;lk`%+s@k zc&CMGFm7~?(yy4sR%X`Z7P-`%1OGzDhx0c@rZp0Ov}z@Cv}Q)xJw_^DHvhHMsT)nqS7RbF4&EG5}R{4}~oFw>9^k>mL$=+pHN;s=jxlKbIk^ z(kfW%=r_>d8$pKFcJ(soG|D4PHrJUtnl9)3ejz9#`t=z-HOsu})jylXqn0UsXkHwsAr(fttNTKl(lP65bZKnF#Wn+tJ%BM5 zeTFIA1+c)vQ5E%6w)Oo-SvP1f}oY`EF&bxqv7WUI6BTeO-o zyUc*y9IKN(xQ)J}qnCc);sr0&qHl?|ZbY_2-qWkbX@s%ot;| zpxQp~|Ay*!AgPtDGCc^Eu-I|XXaby7;Mge;zRLY6GLDGM)GFZRx0Z%@FX%7&3|YLCEtTpHv{HvNv!x%gBvUy#PlMA|VM zbo=wVjP8wp)MhRyLF-A$7VA#s-MQm9V}#JX$l!vTZE)A$@ZmkjN*T$_t_jGW8c=6y z6*AZ$?`pzy9wWo8OM5!S+HvttIHSUsb^TM3UVr{u%9p-* zy4q+|wbrZ|#j8rz@uvFai`Cp~-72=DHVp-(;#&C1sf{oHq*|-y57UzBozkos$tfDe zrL5qCpjR`~D=W#lV8yaCCJDf1NDoO4I;orrrvlMyt=}fNmzZ#~uI7iBPj3%Ccs#^r z>EP37+S};g&*H`HWx8t$x20QCZ&y(15`4{XC$HQ2BQfB2k10QxjCx)LcFYnOcFkP< zl4Y7^<@%|_K-l*opY@h(yB4&;b<)BNTXU~iyKtu!mR+4rN`?n&)pCG-Ay-wp!e_Y9 z^s~VhbmiH;hl{eoXWEkWB}Nh2T{M@kHTr?>MCZXd?eZLV7ZZL(sd@>Wr|@dkzYz!# zJg7QJq^@pCk&m>cTUAU4vdNWXcwHD}A&?#a=klO4?r2gKonnk;YB6p9k6-J&TPUp` zLP@WFd+SLnIv3JvXRZKO;y#wNE_QbCgAR1_nF4io{rM|CZoYS<2e->zK$?@P?fMRVAM9s)_wAhJT{$%Aw|q=KQid zj>2VK5N`pXytmZH4Y|>^JNeOBuUNO)R5&;yjKKX!&0JShMqf8)lf zbWUv%P!^n~D;`4^ehcL%-(GB+sp#u3LM<)avspad3!Y3gPxS^jEfsj%R3vev5BDIT z@4tdf^@{1Rx~Ep40}(HClR6a~E<+!H$C&90SCy|sScOY@LUe5Gwo)KoUVi}ZkeCbZ znD}cE=wwl-82~i&(z44gZ@nr4(KTH4joXZ%&227|O}UIXFBnR(X?{Q^4o|uQr7;fj zmRN5LA6ll?_5$#@bQq_K9ig224mI_k6}#}1$nWyRlk*qc^HRERu+q^?b~8xSi#7NV zIHL#1CHiRns%sFZNZ*`Ad>Jc>r_e8D&15!mZsvA2rT`^U>8k?RcD#R=&<}Q{=aUJU zjZUNae4zsP&F6EP-yVT!)pN?u`l#QdVusz)2dP#M?3lTCFoEAHG8qk`sf`7BfwSLYq|m&T8nhnK*d7N5Q!HtrpZ6(Qc%KPEg^ zujU74Spe-qos?+rJaw%z<%!MXYe^0I%?MFEEjzL+sH(qrlrv}^cYt1SFJ~+Q+H|R~ z(_^ZITM4_ri6gP;Z9aMx0h!I+nU{RC8)4<4-PhN!@$v6RN?OV)iXsy(;IqEWaVon^ zRB+t|7_8U28B%|bC+>#iUgCIJT=Z1X&vRGQF#sK@-&R2vG?hP{JZ!Y(66~5(b$mV9 z)I-tg#;)mj+1C2V^k3c5_h+G>mjrc|)S^xaX}>RYGpw_^Z+Obj&!=l{#>3tLHmlk) z=|~B(g<;+alG}QvRrq&~FGP`!TgH0U-WmEk(HwV57(Bnl61GIWlrf%Nq6WnXVm9av zgJGb!jq*=#ds-M&!T&Eqz{&m}hJXZwgY&-(l{nb`*IdDnj&uz!r|tGX;Y`|}by6ky z+__!?!FmZqSh6l=Pec72Y!<{-!3|uJAHIAMRYk6Nsg;FZ8t*D)1hlt1N-5R#5n)`Z z4-AxmB0-hm!1+Cn#E0x?4%Ey%Vl7L!bg@zvkwQf-L!Dg3_A$f4IB5WY=!>&6fcTiZ z5=3T3s-V;_J3o*Q1y5q6Q7zQs?9cuHmuL5{;5?nb1SjoKgLlOVpozCK(lQbz(FDEaG7S66wrOuF)9;?WwO0#NxO>4m{vG1BkL+9JfuOS zWFN-_J|2(y_MKRdsr_k2PWgTfD4c1QPYM2%$Vy7N!Y9-4Z z3SXl3BNk4FMIq2c>60fWv55BoX1MSVS(}QV0wpNpoc~G%)X7Q!W7LPDpv_Z2L`oF? zVWQ+X#k;Ub90W>H$-ZQX>RcvGEHuoMV;LhtMzBO-y^}3r#v^P)FbIei$wfM2`7Kpy zW9BawE#^#^I*F|tmM##U9l|G_m?~)8kd4gQDB(6MC>=T&DADHVC#JcJbI9W$E+w&^ ztGEw8_OxIL$T23sEi@N{rnt*}U_5@14^Jwm!m<00VGQ)1-EH;FDis0O)H=oTAr!@~8NSFag81LFdI; z%+_bP%YjgzVRp_7Qdre;uYLlU1Ns5L*f0^;_~e19LWyAa?c4t;?5l#}YPx8V;O_30 z;6AtpcXtTx?m7X2Yar+lAh-mV!QEX3cLvu0!696}|LNYR`*KcI*Y2)5ecJZ!wbxRU z$_zTMqHe*plEWJC7~J3rao?79VqEa5r;=is8?oKix&*Ln^$Y>R{$MP^KO;^B(NQK` z{KbHY@7{rtgcFB5l|E4<-dg*>oe&^oYSPcL{>MRBJOufz30*{ZSSz_Qw4QlX^&$QD z+Tw6lZ>mWHaMUGXhudZt?P9)bZ#mP>xVN<{j9ZociUW5I!W*e=@w9D& z6l`B_wWP!Vx6Aa_(ai0E4H?c$2{N@vcEhT@hX5AY}40iAr-NxvLnhADeh2x2yyf)NR z;fjhER!;^Yy>Mbwonzaj6qmFei?oI9!^(v^p{dg&f2tMk2o~M0MVBK|fL8+96C1!u z+NMPV7=KCI(K@z?s%dtxi`2#M_BhH=HmLin&{5(e@30^uwwdeM^nLXzzVg#DKHesw zv(E~XFc+1QPAZFM8;6Ol-OJdUSb7DI+0EF}ibp~d1Hp{z3q5llk@@%)AuX|__Bk#8 z*JG6){Zd^{-ZAke<;pAe!v+VX22_Z*78#8n(0$4%)Sc!}x(agYebdK~PSisUVtx*5 zDN9OM@b}+J!4W&FZ`nw=~Tx-@(Pu=BC?LlRi6*ARu7^pDh;l~l4 z=gN74c9@>l2%M(dR)$Y)0H&-lY45*6(%iH-4+LM$O%?88kE%E0;6QF9S-*2Sm9023 zfH`#N+#yelz&wwE2=ca3qNnPCMW<-s*IZC0c6E`V=QN3u8&UY^+3kQN6$^DOY@Z9v zFAnOd-9Rln*FkR%+LaUk?;PPw@8T|NVW(-^EI-Gs>}7BmVv9TzuaB?m8t``HnhhRP zcw8?y7ar)_HL=+j>$0W-=8v3oBkp8WfjRh_XKwQaCLfBm^mLV@f{CmMRacgH+OjfF z-RKmmED`z61QFsltk#>^z4-Adr6QCP%?I51Xt(}QxjqcDWGL3u<|HM6zQraa&Pek& z{n-?LVlGInM7eQWAF*cd6zC`kZp6o545H| zBJ+24jB3g)c}#K=huBLl=Czj){99By3)8bBb%gyeCD#jh=&e0j(uNw1 zE_>;~xLWOHFJ|iwnR{L&5I-0I5)zM__mdB$Ym8ZjE49nwVEJ~Xerm?n7pU^=wJzkW zWC;02nrUr*ZSCdo%A)ik1DHwrup#{DXDVu#F-@2%dhBQP=bm&>|wJ?QF$zZL7f82&jwe8qt@g zE__$#MP|-H;0Ul0lvdw3)KOk}Eq+sC#Yx;e8_QUG-MQ;Px0O^~TQO~6MHJ6zzUQjR z8tpM*BWcfM(iMJg_L?UORyv>j_wQ8?wVMuw@Cn&7@=mI5@Zv2K$9khflbMdws3QOaYZnq0$TxgL$5?{_wjsq_-9^ zXZVVI#Tx!!k$0n`g-R`X)z$7YLiRI#^G@rQ?6fEostXCRbhl%yq!7&wC|Po;t2&r& ztN!5J46N&jD4Mtq#0oRwGN-p`!UNMBkfkH!t#a29KpS(hmK=^yGyqKQvWO#TOd|=3 ztaBvY!%F>~?YyZ%=z#q95?Aa$XDlIh?aTFmZAKs7^*icwS=Bp4rFs6}ai=r4T|z;I zThV}r5jK$;+F4(`SWl)>FWcliNKTT>OH`F1@SVuk;E zGH*3WlqrZnua53z!adxx*-^jBL+5M}L8vv`7-;>HU>s6!^|orx;nUq+4%}KhE5Z_8 zHQySpTK#&;v})V+0;v`i;0oQ`K*et73&&jJfBr`1)X(?RVUw6-#3=C9Hi*zjs9~~& zpUi)YA7}9nSC^abw1IJR1b;ZKuXU)+fb9W8IlGEueZ9(`-8DWVp;6sObiImf$Y)$B z2{@=&2+q!dh^Y7uj_5fNJ6HSf{ywmOC%)0WIdWmrH<76^kZ>Bg8CDTqt?K;z&IDrt z+8yjejPuz2nDUnOTh_?z;tq{(mwhD)=Q$qi%uE_b*7s#i;QLBt^ObA^?)6!NW5tp% ztQExAKM)sFudivyivJtWu1OUUjedjC1(0n`A$o&vaKI8@~sBko^(lG#W(V%?x60P?Aj-tG&v02(UbQPk>@AB25I{V2OrF{03 zYtvt#vil|or}_fXAm|@cu8d;G8X)x>FAEKbK-bp^*}leuzNQO$n(8I0Y>=RevTiM`7gvr2UD(_vzdPc>EE$4q>1rTMWN_e7P*HRo|LG2!N1i9^#@ zrPQLix7Dd#>@>Q>+N`$Q@mn(i*oZ)OW&wvP&&c9pma%`Q`n;4OJHj-79Jo|{j4RmQ z%N||q+bfla>7gymeq}>(D|F#Y1Tlb?TP`8uXC3H{b1GO7#~7>B*#iW@uwV599Tp#p zLlgN+y1o9{GWA;jN%5R4KlEn{BEDZ8L_s!&oz-@ka)mL^y+7nnYd^w!VcUd%E!@0P z?#Uu>QyDzyLv(A7n@Mtv0z!{ov*stQ23_*H#b&gD%iYsp{F&+x9Ssrxeu``?C{*ex zHonX$+zyet_dh5xnG85%`0A|RKBGvSeSatnBzYC;a;(6q0W6=HzMoBrRZeHXzeml^ z`l3ySmH`Hn{fIZaq7WOrr}vvvBmU}pp7ju>#fq%g4bUbOSKb&F=wEsdI1W6}njC^( zq}f@qvmzv%X4hv?g=}>;`%*NlI{X zZFAbbnvJaxXC9{Hhy6<1N6fJC39pvyiv*{VN^d)%ce9+ZIrKPWs=CJ1n-t zm$w%-iifAJAQj29OT?FyqYjRmql$VRxikuJMF)HNn9;Y2j{MRwVW*0Y*y6D@lnN(C z-ThZYolRokX&Y-5XBxWJS;}dhh7~Xcv5*RU(F03#-s)@gRDglGJN0p79PjJHT!F2 zGVfTR-f~4>iL}iE<_&;OfNF)8v-YuP*_@y{LwSWJw1Rs}vv^2nkXw6np+Xy_V$VUx zNs1s`i=nL?S55m$SLe)xfJOV4I!c7%cV&WqI>3+GQol5P)GM!j$z0*!u77QxDwe5CyM3b0>E0(AM~L%_ybsYjvbII()a5#+|yR@V73qmNb$QGrcsZg z$6`j?MhTzi6Rn$9YxI6i#cb;ZbByPRY&E1j_j| z3B)S7^;}5j)5AO_^9V{Pz^M&vh`F!ff$2bDmf7PEEI-4nh}$qGPQgsL0VGJ<3c0^M zwSZf3er6(IEHY9Ree?FK}^3dSiDLl-({7SFjthj-31AY9PW{O^FpVsmE zIf(Sx;w+{;-S3APDk1&VG82jKIj|3{y=+1F+hr}<_7idoWtia`r>~X`rsQR^`#HLk zUQd-=fx1UIFyO_Va1A=!?(->!{-3QI9DDB9I3{93HBPo%M69^i{%UG#H~DoJJU|JP z7^WtpvcetC5OR@{`{IqQIVf{M6c{L@p4LcAx|IB7H#ciYI{Q<`WY0d=AE0u*$9M|n ze1x}_f;30@IYUBe_W4ZYdYxJa_}5Kk^kpcTv75A!htD%jHJIViTS39YL8%R(b~rp6 zY$z+9`e)emOXW5GPfs1Ji+0w?L&_dc^fFJB0~hM)caZMg^)gCKiyE!54_Rak`!;_a zOy}aH`l&g4!MTF!XLR?x&%kAf(74v$A!l^te}Rot5nKm$ zhTPEdA@zAw%Wy0B7L$Bn# zFmU-5wsq^<@SNYqz{{?^%lg0H&TKZWV+pxGw65sY$xQ2f8Z$3pWr6cOux**L$(MUjJLe^9xk()WugblV$HKMROdNxFeTF}~4I z-}8MZlhl7spD=;Nk}Ia%ZG^zjD?|pDBQ@a%C)q{5kE5%uHlc2vTgoT`p(&QXw!6XJ z2>t5uHW%c<6#f1Ee-L0hOuruJOgV}R~Lnq$5%r; ze~H*!QXBC)4iR2>VpzuM0BN&{q#;KokND4j_7Y9C1Lz^Z@3C{dj8i8BCU68QLNgpJe?~7UECbja0|5>` z!@wBh532aH3FAVCNR;QNllkm1LVpaTa0 zWW1LNfl&Ym*F46dbnU{;JPM{8x3knX>F}c<827Gd8Rg{;iqK>gZ0npE)Q|%=E_``}@L9hGg8wxxD zqxswy1Qh{eh{t#2zF$4^v_N#iZ;C~lC}7s_hUO8fKN{74;TE%cA$>||HILkFP>gq{ zP_ZZuhspY;XjiXZn;(&u$TgYQ*HDx>t!Pd`%1nfYl6P1ZA+NrG&QfiT|?Ij3+y z0VmzYv8DV=6+Cd#my|v`4?;#Hs_Sb8#@#;ZX6F z=_G_K(eouNhgh@mDo8Rg$HOQ{CZq}^DX1r66cBNX8`$(UBqcvJhQlFG{=EKLK|zel zeZ&ACoTbIE^Icin-sq09OV0K3Z+1LZfGcd}H$njuqZNSWp-)C+U-0!YWKn z{0F>`e?{5`B=B&NvcJM_>y&B6>?6j*d%4oiC?aE{1xR9rSrAWT z<7Tqm!OIu))S>U=!rS;=(+2~6wTfz(^ahzlO*h#WQNl~>zlPlaITSWaI1U2Twy3Vh#bpc)ZJjj_ur(+O|c$URr3=b!ha?_m%}0>WF54AF|tLjLu~4Nmk!k zpy;z~gySuNMgGGJgyF7T+0@Xt=vL56`ok^pehwCfm>m?O3Rye+5II^;gEFRq| zX{D}dPL(Z{e<{OwMI?yv?yZYf^L0+$^zWzQR)Vg*fRAkbi1z!IBU!RD!$XJ<(E7~Z z8T*^#qtCl}qf3uZp4TMw>DKP8d0lxmHE~QCgZm6L7P8wpFnW^F>2AZ0ipaSv159t| zOk4#M=1&)Q^WAaBLW$qbL&Ms4&G%U2W$AY`HZ~$dWT+}?!fDNCrp(c8>w+>VH>$-s z#;M&=K743WoipVgBwfMf<`hX?cDw78os4#6a6xs1ZV+j;UCL5oJ3V|T?J{# zygPo8V%2A#e`PUH2git4JtIR1#^6>z&s5EM6aFb}GFf5$5x&8q7Tf>VyESJ+HO}L5 z?dr*Sal#hwzx2YNqSr{R3;P*TIhvS)=+ znAc0?NTx2sRPrEC;tQ#B>(v;}K+A-V26%LK^yQTDMQUvUGe3+1M>9va^K<1#KyK5m zK(D0OIpaSS5&d(6+iv6_`ceEMNhc4YoRwNM-qyh1OPn9tStuo0=sRkaJ`Zv?_j^`b z+x#j1TBkwHe;8>4J9CeSKuc@fT9*aSM?2o)fGSS=`&&QdjNtN>Me1UNCOlUC*Frhg#PQ>Mjet0@CIKu15gVBD-a zJtGyYX+&~W$nnBNb3b_LYYj|a#nOgMvfhWd93_M#&b5qY(zfOsc*Sd_Bp2PHivl)s zm0a77rjERsa;&FFC@|}g6A5J-zX<9jxrWT#x(&KhET`{>UsC8~PbJ;OOv$+*0yR`r zpfCJ3GuRZvf`?1Yhd)-)PeD@|4<2ZX@*Tz$ca=G6EV$aAf6Ghw4Ovsei}4^!S9eRJ z5+H(?dsz)TGZ>6lW7(#Dju1vaG8%P2f43toZqvUaluZujc?;sc1}(qc?%#||58+Sk zNsygiZhLp|d@5k2{`$vz=zeEn3uqo?c583_)#mRo>l<{ZtJT;E<(7H$lJGczHP0wL zSbVDYlNiGgy*T$2O_%p#w5R+LJFjI<`-jVuK&ar*RdmrN%ECF#D(d?zodW%meP7pz zgiaFx;?3Gcuel*$J&tJQH=Rzju6^Y50ST6{txr5) ztM`dY{_F2i7ZYp5a;W0gfATy>B0g{pnYQE$nH#Ax8~><5a)6~6<5&zq`hn&Oex0M~ z*)6{jByYW91^Wn}G~fjd0TENge*Q*Xdb(QyH9*CwdRMN%?ezj|WBVuD$JYUo8(SMy zi-Q>rgZpz7w>1Zq-u_@;UZW7^yZJZ|-5)oD3!5wGs>3(88)EWpt*z``+vZt4T~t?U zu?vkkTRpG~K-2!$*U{l`Bq zW`*q6e$N$(#uN}x$pf;LjFSj3I79sVCmHjHaLu|OeDRK-_oD{M1Bf0_zd6qr<_U0^ znu&;uI#$Ayw|3_K!0MxD>ZQ1$TdxaB7Bc27?`K8tH$!oIxmJxhE=?r0N?7m9c9wO* zk@JnV^rfC6q-lRoCUJ|pocf7XW19Q#AFYE|kWqE|zH|yxhd$6QdAL?%kE>CVav6+x zjl4>Ybyq%G=o}}4I}$E#++*gaukhSj2zDFB^sueO0lizQQ1<&$PxF6juV)Ifv!9nc zQ_U04?Rs2VvvrmVY!^fJA3*enBw0~fM)D*%rO0V#AuXclH)?}Kk$%YhOAchnW3zFZ zmGe0&^v$2H1;(3<7CtSqwF>*2{>(8W4?e?w`sE|?4~bgkLZ3TPSeL&=+G_nOc`*@5 z2*a9k{c2kfN0(JuJlkdEYKBcXcjtx2X@oG)2_%|+(UU8Q)1DHQEp}J7@lLk;v_HHh z=EWP(vTh0Rnb3Wi=^K<~H+DW9V&Z--wTwCE>GvS{w`gS%( zB`c2w>;fU%w|A={PHnQh(aFAr78_S3iK{8TEIxSPj^!-j3AX#)-Tj>`gx>f9HRl*& zo(;Bj6(N**k#b?lv6Mwn!>i0_c_PgkPCh|5xp*^`PsI-fXjAX1ISvo2XsIxH=f!#t z=j9*c4BpmNtLGIA*t^rq#zRyh7ytUK%jf2a3kz`>~FPmC8So;0paT zgj<<}Dpq0SI6gM;r$x8rw62{`BKr=2P!$lf)RH*TJ^awYvae z182X0YCg0vu+QB%5xQjxTHNx5oEv4TS^ejaT3hC@h0}xWhO#7;d!{wZd+A)oFHP#eWL;h zrMtX2iGl+C0mJfr(O2A_XM@wgLC4G9Vm9h!>~<|pP!L2TQxnPI=Zn{_j?#8k=4Qhb{W^5a<)mItHFP&oAiK2S4iuTYwk$8yQ=FE z?e16?gUstu&sQ*%1Je2lQFVw(+ZrN~!&e`22ekZF1uMW5lLChe+ORL3vCZ+^Q~hDkt)a0x6}x5_8b}W(6YH?$R>+ip3tshYY}*2YMs7 zY{r9*PPT}ufGOae^7A{+SaVCG(>+nN`4Qt9W2p6Ioe`kG9PfEp>gyF(^h)N02_AJ$ zVH@&)>t*zn|0}vVhzNZNCR*#1)7v@=hQ9d3O;ROaa!yfkJ6$J#-nyiNzBKh6B#qvh z0Cw?$9OCnqx=Lb}&JH^dQ+UaEdbWJ>W<|d!Ylo`jrUH+U{mqs8K7qRFmvFAfG8+{^ z-Hc1K*F%|SBA{-DrSCVGM)9L@%^UY#YNQPhF)Hs%Ja@^N94Lss+}DrbbUAe<J&XzS$jT*4`20QcWuoTln*PRGU%tui$bnC67fQfVK6wo=b}DSDBatjkzbzaDk8F`HC1p6_ zDql~v{9{>W3|83Gy6^utv{ByYgEaFhFFMl_uIMBd{etEl?H=`(H;I%AEW{k-gsSG+ zwo4y{lwuD58WP=aKbLhoYb~Qo2%IGVVbR^I%aQmhP=Vx9pVfzn?-$KhMAAE0-c@~p zOF8fIa2~)DoS|Up^KrKw&ahw?G%Y|gp;|-@~KoQ&cAY>X_mpUH7q)U&LfQQ^0jp1bth%Fx`k2~C> zyJ;&G7L*H88W8pbloD1JOO>q2< z@1i(gR_eT;m^_X|Aos%mH(geAs)p?q!b!Q4LE*lOh{Qdu_d7X=V1(_FX@OYWp@vcW z=X-8b^KnuQqq>9_=GJ$5+8L#L(k3h)DHBk&mTxbC04X0Xa1n};f+B!&tLm!h$hsiV zis)UmVX-a{mDWObKi8?!iG{Um4yp4(x-6Kg5A;&o@$Zld$$R1+HnROIZDQmtcDKiO z>j!$FY5I4^#lM5rCghpcZH^{{2TnbGsgnK4A$Rp*g9%dl2i<{nK` z-L(3=iZa?-?19cvJoJTe=tb3l(?trmr^AE@$*yS0n- zqO3>PqnFsJX&gr??S-%vzvhnR5FZ@HR>rl5oVz_TYkUgtO*u8xS|&IxCnlP}BvZz6 z%XGqR97Rxe+H_}pIF7jTN$Kl4Y@+YJg{EQSb;w3XKBXbkwgoJOVmp*UNzezg zr%Tx71Q)e4nK}5tD+7X*`;w5jOAWa*+crsv*&mh=x=ZPe$gLHYr~sOcFOVE&Sh?Mg z*CCKknUR)U;nmgu+7{J52S3LhV^Xh3)#GPCfy{02?%0vZM3$eb8K@=*OQ0-439d(x zn%#rGL4EBb{blo9@C}@x3v6_ztZ!Ow%&kL>#{%#-ZdfJ>-uwEYAa|Ax`4i^#q->Ri z8Hz{^`r#}5#S}SD?T0T!9u#_!F6d2Y8%H!r#dj7&kO}8cCO?n+71DP+j3vdw0tg79 zKnK>s%I7W`0sOojmbNykpkYAqcOd3eAt;)T3&mUIQ;!s+ECJ)VBaL>hl{Fz~E3@y5N( zz!-VcddthnvFjwfi}>rt@rh~?0P#i-7(VJfO&qyH9ID~Plz(w);r@X!*>SUMW!rl5 zncG-Fui>`^q;;_eC@!aGX+vQ;pyZczfe*dtp|iu3gskzev9yWQ|%9>w_Zz95bQ0@ z9eqmoszkweTbibeAn&}&=M}}axH50s3gNL=Cv!X#ivHF9;yv*%3=Zq_SB2dbvXiz? za|=zEXd$*(d}{iV8z}7-Fef9xrP;EpQh>vS=vQMNq$=Oz6YN;hPVZvk*zd$Drczap z=Q15(OGnj1nki&{WMm`bWmNfz%Y>V={%wwB95X7?lR}G{z{A@HclLvMlPE)zPd$$< zUyVGvtj$%)Qol;BZ5z@0P&3X6tkuHp$b`cOz2)ST&- z8ojrdSW$23b%Va`iD-xSS`;Ff8AruV)-1jfSqmpef9@ zktOxh=LI2!53&UZOoo7HsYwM3&h!fu5e3|+Y!kcYl7&c;%&7M+FsbUt&)1FNi(5R2 zUG!_RsdSe4tqR`9>Mz70lxqCQ= zzcVMrcgBU^bTuw^;Dh3yF4(oihnJSasE!@py6Yx|{#m+0&|jEOg6;RMU?P7V(A+}m zGQSq8ds0>v$6G!d*3I4*G~st{CA1KD({J^y(YI^pHA?UKmL=scy1IT5GMV(OccWDu zByYojwE7`YeW3$x1eSTH*g%+Hl|8Ded7(T(i5y1`1ll!u;o1HUuV&rGcKa%oR@qqM zL$WFREh*=$bW10(DY!WatM!Ze2ZhzmRO$@~RW%Mnb3@+?d8VRVc*LZC&UX31O(#22 zdSH5|&DQ;p(Yxd&;{&4617$WS0xhsl+J3wL!x)ulULgT`=Qy4-U?OLx* zTUA-0>8lVY5!y(%rsG8;aYjLP?N(V_-TUUbw6ZGuO45BTNc?whfnbX(*X*sP6Qq03 z>%dl>XbhC=W9W#zEvsw>89IJ4JY32u;CsYqK>=@Ld37F1*Czy^pR8YgYYWdD;FtR% z5~ItF$Ayxr;j7aY2QkrPKQZ)=uI6xHFKue_f?TS!)jP83IYQ<~zy;zCZ%xx{Trj@Z z-Ho)`p-<`X8r`2oq!Pf7dr#I4$#2DGr}gTV-+{XCghY&x3EghbL6<=J<3HkPN?s(Z|dUTRs`E@8L;e@oGYTLxAC(w@ybVVc9H@qCJ_U9A-uc`fv z2OzmwzHAZrft2r`owDEiR(j~^b+{Rur#`)Wbn_DSWPL~0yyI&Mo<$X8n7rc4fYy*cS-};Wv1(nR?=Y3A_IhqkxpXpMK#Kq)WX!&Ku0KeqB?U} z&z8t1{wbklw(@_KqjSkn207ygf4wid!2jG*QcF$}sGC4ykDs!HC_orD)eS$f7hidh zmplQ_jkN?SFwvtt(RU?4(BMw1`ky~)yZt|Zl<3yVhH`4aK!EJz-t&+8r3U)YmxG*BTsjH^*8+ZAO|jN`+XTLRt{=xzgom@~*ftovgcs+_g!t&+qOHlI(j>{ZQC|Gwr!_lJAJ;r&-lmKw^eIYt;<kFR>B$s4bAtptfpXS_tG_F*?Ty^vv%VHYvzT{V8SI8hG${b%EuDT6ZU zC`3f?d8*`D#n;fT1f|*8SDyS)r98~{9&{Z`WRwap8JRRp*;J&Zy+|38p{7@aU<&Fp#PC?SBtfTqhw*H+m!b@p-LbOdLNv~dLy$Csx)LwVBubsNw z5i#}A3-v^=v1wm(Wb2`XP2n62$Z2k1>%bRr(xRP?R?7;LFQ%bZ( zoJ<1vASBH>|H`g(W4TyMRD4EyFEeC#-^NE{AoRPL7n?OFUjK58jsAdq34X^)kHbiE zTjz;IGCRLvsWd5Rr&h9vl6#%?PLlaKIH(BonIa3>nM;{ML<)h-j6c9Eez^uxN4SNX zKasNTE&!r2Agv6GtJ5sio_7PrVw_j|6g>(QBRj`L^&{9NCzOF`(t&a2r@@M|pyA60 zCGdOwzVn7Uq)I~<0n> zv6Lx`$x)+dnvNQEs#`I(UYGgEknW+)#B~$7bZ!+oYn_q7Pk|%*HHktmj|3zdHMsdfR_igSF&WQvkHTH zKkEpb6`T^H41thCymZbUjzuwCMd@2kI*phnr4<>#kJkR4 zL@eGD&+nfjD#)*q2&sztm=m}|JUnR?Frls;i zC1?q^c){H0aW6r-R>ng0)-9YUu(!cB9GQiOiD*oe!DV6+8`<8eFP2LNY9xhJSVUN! ziEx~P2$@R;uY$`y2MllQ9CltHYq7#@NKYI62A0$`iP`Skt9N2l=v%hC^#J7N&d`xa z6_mp0Uxt0 z7kZ!SPS-7X06QAyJ%TYH!)9i_mY-m~ga0ZCCK-$v4CQ53p%@8ln6n7gpm3%j(=2s# zuIp3sNAW4hRAe=h8Zsd>i+W-RB=-CJ>=TDtuVle<=UTN?+(y|#>n0mSZGjH{hRr4Kd~J8r3d4jC>t`C zwYt@tXX{liiPw45J)%s3@bV>iD_7o@(67q*J7C9obP;I(IywPNl@9!rB>R+upAuKNyRGW1+v!y|nj2BI*g%nfOH(Ir zeXTs~cynz>Tv7(6;%qQ~BR(rf_Esk|Mh?apBH6=3iAE{2ACy|h( zs^EIYHP`GmM6-JP=C-jj6 z>9`pdA%?Zw+-k?Dg6#}U16r1Bh8@>=lxQQ3rA zBE$l4q88~PUFNY0R|~Tg*v)=CklGBGNk=Hu2l{z$ZIH!DSJZ{XbMFIBDYE*i2&o$Ru zxZ{fn92IwPZ9*6RyXQFDdk~?5WBkg^H|=9P3o$={684Q3KDAMyXEyu-CNgpfj%y%8 z#vNApb7gJl!MTBLVKhNL+l4Qy1wj=#ReOEZb@zK?-s}PaL#402B+OyEIhEA*nt#y_O~pe1*RXnNsiOD!6*eCMJd$FJj8VKVm~6S&N>>F9woibnfbCNQR~A`%W+> zd7 z4JcPrBB=-{VA}lT?-oCcUeFD*6Bph-O&*vr5RM~>R$b#Y_(b7T8WaPC1b}T8Lg--0 zAC%n=by&A?62dQQh4YWrjgV}q$G)oNj3hXNmHcoj-nDcYn(E&u#M>`ph=Qmx4Ai9i@S`_n`cyJ2}n$qB0N-eL>O_ zv&fF*Sr|-}oSqJDS5YEYn28FCF3*FZ#P|_xaa|XC_mZ^Sd*U3FXKJgYwA=<9Uldu| zkx>#81#N^Wd9qOG3^QKo7j~Zb_*mccN$R3zrjE{qHMpjNva}a&NouRXRc}lTH_bmB z>G0!#%}WtNK}BVp2$szqj_rfIp8hm)fsA1^%aUPfoe9+aFGlbMSY(quwx(sMmIr_@ zhLb0fAUjkt0}VVh)2pbt08_sHh2hEWcUh=e~z>X>bM>I>!6` zf$7Gh&k)soos0|zN6IIRHOg2;LCslZ=!7!xGU#jgmN`~!NB@(MbR;uRS;(IhbYV|s z*2Hguza>TJ*qor4_Mqyj;OZ6s|i0`vZ{PE){vR7dtg(x4U) zzcGhhW;)W?#PB*uO|H)QepI$K==YQarZ2osA#1y|O1!{YFrtR&Wop?no@A+o3bCAN zTc#9sWpST^3U@?FwY1aSekoSmNAi@^E@37%Nvqw!U$(wY)l2cjWriztTS_jiRZK%z zO7&BM?6m+^@4xdw zo?C)aU1cnYkER03;{OIZZNy6FY=)kAAn0hq9_!=0O)+0}{lIMPvfGG$OdofQyEk5i z*2Yb%Hol;#;*tH-z3`qjY(a=ehea~9s)B-;WyomHQMG!h$2GBo`RO&UDD z8yUpn|A;R}PrSB|(OvAS>1hJ6-dcOw8f!ME6*9Tyhj}B|*Z*xT7kA*7AB*jwXY@1; zIP*_4vFF3!Ncyw1C?9%cnpY1NYl)Y?1lEsy61vznq-^D5=+i&THHx&{=|#4!2n$?3 z&*Vo58J14w7Lv6fk>ZT3+84$i{mIgM`eK+^&~JmEWIHC$8+GLuI{pS$;G@R&_{9`m z_&DfCl%enMtEJy(xq3Vw_>nzNA-n|o{c6LHJfOZ2P8!6xa-27Zx+~U+A(=UFy*Zbw zcc*FQpy@tgUMoc7be-=j#bSC8#XYH1eYoWkt4Zhddd9n;p-A=iwD@3WGMN5NWVeYna;xp>k588#}Cnx9* z`~hAIGN=m3zL*HJV^mv;9_qC2__5PPab45D{6MxmOwXMcOz@M~(ePXpL;Qu9dUbtH zsW*|aF)3*(KUFK4H1`1pi_jy*389zub!)+>QC%e&UGq9O_K zrx%{ywTGOsG&V4f=-19^fi;P%$L&-9v2vBRt3^WbvAwlGdrY)(iKOE_hl5j8)MjN% z3{I-NdNYql`^5!mq`Vbkj_X7KNy+jbKR?6oTW&aU*D7y^vXTPAVHx=6Odp?_YhI@M z;eX?`lVzzh2ibsYAVnZuv{n%gt25mB}9SAz^a_i2BP}<%oK}}E~q_Nhrz<05Dq9w^r_Yh4_<9C%Xqa?7_1s28# zYqBAptvc838nvrEF@L@_o>_zd-Vv4&Bvr|2i;i=*Y`%g|juuS;eKS6<7db}X?+=QH zt4fo3f{a36!;Ct|#=zkHo%K9ztv3rQZ`twTVcpBc-|bE)zqyOA%6%fEx|PAE#r>H9 zcka5XpbukYPfAL=fG!;54t+v?{~ySBy)FK~d#QZ?-L}Q`(F5OJ@a^_UARRwQJok-p zRf9)sU<`s&y&%@ud~e4i>bP!~Zel$|XLA~Kb;8R3H6|OD=mDS{Z^ygvoQwZ7YMy!x z{mPZDt;XIpB;TE;gJ*l&82jk^z7cx~_m}hbo7$kvswW)gZB>!#by=fXNazoCke zpNtGPkvq)m8aW($Po@_D9EA3AeMUVZ)$Y4@T*&dde((MYlHhPTt`)tbmE$tN-~0XW zyc`ri?^{B99-#dRPXC3!jdvxS9ul8Ay9J_`S|nMjv|f#>8z}2^zR}fl+)Q6z=Y%e- z`FJwBMxd@ZPdR@Hf=^?KJxt#iBSKT;!lv}D!^vvrhZWot6zIte*B8}h;rEEOvrx9{ zs^xWYGkL$%kfo;U{CY`d2kMKDxBx(R^>2^cM(k~{0(d=@5{kvB-)YHlF%wzs2rBFlA$Jo^&$}r#y24Y+C znt`7y78s7@U}HR9YE-ZI&`qz1AS&FYXcqI2a$ zWSkegP^D%($UI0RXe>|>5!hlTj>mUihI$pU*u^(lr*0G_>S>e4a48ZeX=CB{dxUci zE8v7tcR@#3TKNNCv+Mc+xumG==Lw519KelP1nj!!X0nOG{n4m(If|Z&xr_!W&re#d zj^ZkejzzR_k+d^jmCi%ze3ckIzV^H=FjE@b-b#ctrGcz-jjFl!a6c zhNcZ(Fo<+@`R;*CheGJ97+fP-^$oZJSzi~fpSYP)g`wyn4yUb5Q z38~qu7|(}9q@7;M?(6m~$=LuK$QyyOabDQmZb=G-e$SsJ2+ZCRn9B?*bw6zR6&RPY zIEZKh8EUD^RmK`*wjd;f)sJn4NU}$}Ct(h;1)xN(JXoebgCXmI@T9pfhlBTqCxDG- z<8Xp14D81sJm^GiKaix9WJC(=)}eZN8nvx-Mm98h@!6lhHn$|B&e}5qe#Y;@(nW&l zSQS=k%ZN7MgGnydNF!Q>kqJ`GO6_3p$?-APb-6Hi0WW!bJcP|jUVQVM8lNIeu1ZB6 z>c=V8t~zj+1bSk$h%ulW;5%Y=F}k%~9=}txV)5eTKTe!?gBB;P{;l<7ss`f++975Es^@%WTM3JE z{}Uvz_ynI>7aUQ%T@sk)2y$qB0-f4GEV#q7q?Do?mcjFpWHYPEq46!V8|F zX0Dv4-?H+0xhV92dryW}N&daMAjNNjjlZ!vg=i50*YRfBf}2-R>=g!F>;Wp+SIq@J zpD6{{l4jK8VxJr>eqV0xeN0iHWO&T3Wdj;+P|A?1&;m^LBn2<4Z0YkvwRx?$b)67` z^80Tf$-0K=_&O+FBrpiC2CxGxw3(;JAn|I95FRRM1lPZTO4lDyp$oe!IB5W}#S*VyOVv?axBy*Rq4V5|i^N?Vz)^k12#f zqqW=AL|to?>WbSa?fupaOc4dRf8_N6#*q;T0Vj2o3^@k&$+)Ji$8-0k24N{U$EkJE z#0ta_it)DJ#m2HTZAj;mu6f&CSj|U2K+)$~s z4EsZ#8U_quR67o&N2eZBZ}P6m+T^u@mk6g6*?K0=J=VE2w@UMD$-gaLSya#za0@WO z(8HJ!ea!U2nH)k;r{DAU`j95gbkZSAWG6p(fSa0=+B+zc%$y*g50ur|F6GIh7jnmm z?_2}MTVH3)ff}0<{4)CTY9AS2%1;I3qh#1pU;0lZvscA|d)tg2e>>P5O=PGbo1FdU z=7xfsQVFG)0$qOiieE2yb#5U-c;fxhp2o%?%)5BSfugucD8)K+fG4q_(p#r3J?3yW zz>)0LF?UZ{wL^v5R_2sgUypw=QPS4Kw8I9Lzqcl#1F>f$34fqIaCZfM*nf9dog?%g zFOKLx+m^R78F0`Gwb5YMTD5NSiW7zuG3LPMb(9GvjFOvSo;)hVo*Fw^$}1FM2GZj@ z?Egx1DSc+0q3cmW;4}SH;j_9Q#6>Jca;pVVhZCb5em1=Fzkdu$=neO;v>t@JWN`h`1_2m{B1-lwp?SiRL_4jX|f_kH>=>@%yX&-h*qU$h3* zzjssgvFQb8FPA4%q9-J|*veb*Ipmk;gq2_jsPhndi>yD45AiiY_hPjMhtmKQYFx^T z^GZ`g0}_3B&^630CwnfDWj7Jl?uSn0NUM*-^e_ z+gCiF_$~jy(DYZ;;n@9Lq6c^OyTI&z7Z&Y``7t44Zj%B#Bi+hDb9RP4Exou-894qk zL16k~pgRrK-QV2mat|1RTJO%(FaSslF^(2qAzwGR=b?#&!5; zhQ@J-IaF}q@_?^G17u^=ky7iJjFs}5zzgMqa^jQ-CO`GkN;A9Jsk_c{miojeh09v# z*>`jfUGLKF+^IV*v~TR!wv5SuN#_2L{&cv<^hu>A4Ol@2T&r!SSyqac9;e1y(&xyb z=1H$?^UX5Gnz1@(LK`qB`19=w0nHeXE6!BnI-*E?44qfz=J&M+BBFP&*sE@J{|H+W zTN3CQ$k`8Z0nr?D+JO8O5-eZ5pJ&2Q8$VFr88|x?hjDyZ@@xa?q`)=Ls|`FNvaUu0 z(zMHH?=$yux2kX4UG1T4G1eS+TJbwOPPWW~Qc3dliC}EVQOJR{e-DWYy5=f(QL^SQ=!N5OjB!OPflNwys~a(8+>o1ji1m zRy_Ka1kle!S*u%;9yD`^c22ncB;m1VIo5}#5kRXoHVHazF~U(H@%r>Hfpnq8&?ED~ zWV3}xeo-e;1HXVVD4v~s()M}$*XZJ+N!Jr#KI-u9|1f2(?3Q`q@#lPVs>wMSyGYTQ z8Y1T0l)oI3ppIqny`EsxGue3FmO#?ZKY#g{=zBUl#6dSQ94k+(8g<*4SD{3I4EXgamcsFLop;O5>h2HRZ(*$ zrp@y`P0^_{&)t&Z-}d8X($u64?kb3>vdD;_+%LW#NAczltbiDUuWV2jW18pK6<rfu5(U+jH{9zR=j|!3_MgW0)N%WLsL>U&-}cpxL>eQqR%!IP z6JW__2k^alPd}WR85nw4X?9Ec{Wr6;4*=AjS;V6Z8le7ZCj{4XlXAZ5wYrU;8_MXS_d7gLJjCbkf%iYL_UgbxREFLv)0(wjujgf!w3q zfy%Ks<`L6X&mJRH^nqesxuIyIQ^38~L^TzL&2|$M*{V5P9(g8mfx+#=pKK)jAR>_( z8l(;ac9E{ht}H3Aik6dtu_|b`zc)ASxe~UQMxa@Z1Oe@>Z@lRti!Vc-YM2fNXqW@~ zc~aJT$#_;RlH+ZjHXWVRs#^h%XieQI?P1#k{f-=avAfjQVJcf@kk=(-I*jL*E@&%-j3w&L&a*^oqG1kblooies$UuWP8tMqzl zYMdf)_GKL~KZhhGTMGk1YKrW1W=<}FSfd-UM|8=g;!B95@Dw1DN7s7CP6Pn5uqIMd zIUAL8<%mlNjF`QeP{%^!qekR=6qOmNRVz+gW-&rUFawTn~s3 z`B8#|I|PyL5=+)O`?AbRb~8e7F?*J9%SlIk0L{55>~vPRDGJa7)w5r(8$aMJ^V)=m zn{A5OFnM#0@KG#}3J^0?$FxO+ZeJhe{Gm?B9Z1Tc5<5pa(QPx7=UT4SYIGaS_VJX` z=VNh&GzyY!D6FKpUvhop5cvq)e*B6^SGO`A*{Az+v|^Hyw5vQ3OQs|g6@P&&yQC(a zMK-G1gG94`r3lCEXpX1#enb&Y6MRjGbbwJ zU{DaCteO0NBe4|_=l7VdhEzlSQZ#^KV zjN80oFwDxy2X&47_ZBOg-#v+O@r5&Mm=X{^Aoa_AlC2zF-mSL~mP{Bpor*u^A&OfK zzxlMj6K5VqzpK!`BYqyjK=?4(r>?bcO&d01HG^az**BXEWxv$?!% z9cz83Gs=W#p=95t7CBFF-T9t$Q_?%t-d{a+bL@A2Imc{%E!DA<4=o_%`E-&%5y%QO z4S;6C+i~t?2yEGLQrvd(KVT!@eeMf6?Ox;l5A(-JyITUKXiBxOhEKW^rD*#3OBy}N z*`A_lDf$2@t-;{G+-4Rg-2dhYtkV+6pd3ke z>=aEoTgAwr+-duBVE>i7L_h&${oioLr^EjXw=p1q9w7g3sWx^3Pzpp)&LmVwiZ*}- z6dmS&TD7&YfQ~@__YX>dgOlt3a;IV4=~yx@q~05~C3x)w@h`AYWOzHUONK62y~~2l z=c7b|L^(qtatYbWzm22U=Nb9?tu?9@)Zf0J(!bvPoTG+?+RXco)D z58oN?M3q?WI|;3s_qP~+14iUnLdAbJXbqfZ0KZV4=RVe{e9Sy@4dI-j;9p8#uSVv=W~jQ*)8*1hT@PVqYM2cCJ)%KA>;ma&T8M_Ikp(hZ?z6!aDPUEB<}$ zx4E9Di%2R&LUbL^r^BXKwPTE)Y++9mSfH)!dUS!Rzmfd7AQMo@N|2s7c)y7z%E2-! zcz5g%!V9&lSz~z!C>9K2z2S zarAight*dcS#+?DtFRIGBb>Q^jgV*uuLkDoE5Pi>`otr3r)O**ER0wacJ$Xqjq!Iv52fU~rcgVHhK2-g z2*r}arPql%NwM5%O!+zFirfg`q7WQaFXF?!WK}&e-iIUe<=oWdAk&;g$qsJSAhYYnNM{EPh&}jmeL-he8ep2I}Dp3SbgrapQTxXCu5&rlPi$hW% zGZ8ln*wB7>*SzbNj0VU#*bm!bzUF)!Fx>t-PZSw3z zb$&ZbXo>HMc#&FE=gNV!gM#r?VQSseDLQP8IXR%Fr;C~ys8Kw{{!m5=Dyma|Y2)-y zJ3hTaN-*K+7<7KMBW&_dHiR*D$60mOFGB?dfDD$>`?YQOJyHhl3d%MfN@3cx8-2!W zeN&1N8CJesBDl5w%drJoRHJ|XNwEujFk)O8 zN;8zEz~mwCVe&y{3LqpUjUZkN`f-dZ1$y@)fKE5V`v@1&92(*$Eyvi~h2*=i+$_QAWV;Hx z^gtMKQFjSg`-edLYS|(OGWRaJd-*{@nR35%9;k_mMs6m?i;9Z%&|q}{psr_`y^sxO*S9{+{cL$)J`=ZDaioW0V42J%FNRA!L9@- zb{X%vTRVa1B&8YVt4B3z;6nSUcE;xeA;^1@ z@uX<=A-OpI>}k{5QzY}nwXg6Y*2B!8raN~b$n;>#anhTIHIGcw4*kJ*YwBb9IZJ4l ze*yVJ>fMXX*2DtNfKdTPC{NKI-cC5Lrik7n3fkPstyZO^If$|Vm;P?hj?9|Jry{#{b@ z8xoS!H!E9m9EOl3xbTJX8R#g3t^TK)gTqi?pDq70pt#jTLwBGy5}NOYmM8bzH2J8? zev7j*!w<-PH=6yyUO_Ml?#LBP`B6KM8wJqt+d?+67yDPnTp_c~n(*Q2-*vo^IC2)v>5Lpe6I~W~!nb~5qzGAjS zPoG&B7AA1my@_#${SH=b79$=SZ@@?C@N{d#?Bc15@PN0Z0%9m-eE6ylA%A(x9x!#v zE1|C@n&SU=b$?qQ^d6J;%wid36xZyZ`}A60jwiQuF23-pPQ*n;r1aN+U_S-*LUNwf zU%sR|@^%Cipa1M`bm?j9K!KTNd7r1&9gk2+wMqb{(>_rhwu;7@OXjMO&iv3mXH(~E zOjn?3H$w|mB_4MGOOoo$EY!IN6L<~}Va{>Akr0reh;e?@jBwzqwCJo&kXO$TTIC4o z#e71+9ODJK&dKvJcV`Ucim!RPV9fu;=HB_tIf6pz4q5_KqJLhzpVj|aJ8ihD-_&&_ zlqXO+0=bvpQJL8%c`k?k9L(241<5?$3dXzA{jPQp!qSHX@V8C~Bm15~0E5PP{60BH zH^aWhP?Ra1PUwhOKOK*Q8L|o*wZhYr;Nl`;hQ2$t@hh3ip|jjUGZx5+$OJcel_6;J zixRs}OX3-%YI*W!hQw(G)|n~vKdOE^y!zhvRJk}SG$9eSmPGQGKJAqR?O_~TF!}U# zYUfL|3+Jc#L@(g#H!MdsRNE7)x=TdmA;tsdH_y36eWJujvKWd!$Re-Sk!}8a?+-%X zV0~t;X$d$G=wPfI|0`JmR(2Nl|En6Ob>-s7IuLtLwU&sSB+zIoAaLAa;_2fzDgEGG zoVo;D*99*1pKq9G61{^ql=F(z(Dc|hCYy<+bi!8JEF*_dTQbA{kA)g^I{k6 zIkGpLE{&ZX1N(@7`S-lRuV52h0P3ps```KQqEH^82vV^y#DFwPC?>)eSPjmZXcM7+ z^b%SGQr_7C)z6;3!m@=H`heb~W2WYiY6X0ir%`sM`QdY={TTw)Z2vrz#mgcl0Lc30 z`vNwm?XE<3XCBxCET_~Q@!dx~8k^G#IIJg~kysRT$9=9-bpZJ8i<)x#q z^8e$8^w6;k4MnnazkQubw7(CYS$WjlZr%k4`da=V` zI-6NeDo8v-X-s8dj|-Vop&(xq0Fv_xHU*@sYG@ZGy`r6gnVK1Ejx1Paq>vBx`!Fvk z<@(Us&${GIu*I<|o*j><9dtC`cc$Cy58SB;Yg+vc*3NKK>KtZz07gPgnY=wHz!8nsvB3CI1V0pw)n zfkv`jO+GH+4ju!`umbQrS2cEs@e+IN4q|ROZG)RAO}kcmpfNe)I)ud2(w4qi9=$(1 zb?UUs8h89q4GwzG2@MKSpApvI>48WvOi%uqI(jXZ(;R(ollsQ`xu!-OXIk($Q>AcM z56Mj!6`jCeNgPOHO(mXQl`V#8*xm zhKFK~DztmLzg#L5JE>4VOdc%W`PoQ&=;Y(vZ z`hC;m$*E`hdr`*7HO@&CT7Rt&CDV}8pjP{*Q5l2z+c*3BTStAoR{5O)|D{cg)s>!R zjYCey+3W?0?NP{K^YcNC(WW0P`zF@PLNv$zPZuzPw2Xo&ZC`(>YKy?|vR0|ugQ1WH zH?6|5+1mPke6q-2HsMhvi-4qsX9Kz#M{f=154|<3>bHz^U42atdr#|g9|B{I&4FZ% zaq8>IJ}{d|n@F?j1*`sN20+Ui<5$S!DNkz0qpCiaYlI0Dl?AS)aMQ1lSU8FZXiuj_ z5jI^MQI^G8T&P&e?WZYkzw=+29FTAxwtgGoNL}NFtxz9vm1Jh$$UvzpG&UFoPA&Au zMzg{N!L2xtk&MXwn!;vV-OY6!5Au0XnS?E#qSwIm-XF?ildR{+;$fBd1GY*kkb${j z`=7O=-k!P*o}y(tDgT*(c5>MJ_#-*!A~2Qg#Z6{5^_`s9i6*Bx?`2IwkD!0aEdE(^ zs`pof1>^}8d4$0z!GJA0Ad}tKeB5*959pQMI2f;v?P5*_Hw0^|xlxpKMT>Q0OrpzA zj~hvf1#WCzv?H4>#@kM%*-&YbGfoe1F?!zzG|N|^4=$#cjD9u8ni4;uKKW#h5yn1* z)pRqbHY)q1$?nvo&~!P2y3qCjys##nPEhAGqNW0BZJM1NYG4{=ga3Qz+$3K1w$TcNU=yEyt`9VhHyy{S>rww=IlLAd4G`pFig375tEzR(sp^bcp7f%| zFWy`2w{uUP)lGZ?m#%tgQ{ITyxN^~SnL~l^VvntocS<0L73qkCzBB{TZ}Y{b?~t6l|*&u0e>0*le|i03TOG@J6#z z=C7>#??1Z#TRK!I$qY}!#)++B5eClhGTM~`dalCX0`dgw?8je}mqIf21L!eyd$le3 z<|BGIpZjrxzhLL?xZ4hsXQC+rY4^eatH{9DBCyfE7ad_>N&Jbs4c_dWCA3{o0FK|E zXo}{s8=t(`jwR z3IUhr2^6}z$K|~;%#W!Foq0lAu&UJfM7JgYl=Q3V8nu^3#UXU)T{?fE+|?Mi@tyW# z9f*3*#_MKK_=$b6<{|0%#$FTa^m^c!utDeK81X~bJIhT{emCq{T$rL?K~U?e`;Trm z-7S!9qnCKCp`uZtUd_5SM=A$oCwVl1%(udYOsMxL{kOYJlmdG&8AbJUYCNx+}51fhw zaY-#ZLFy3+!^W3^6182ntFSFr&mh3w6wz7>dV%>^$M>>t>&QF=|6Wtttt~!>M|q3S zpj+-@!Wcm7~D6uK7S*H+pbFn^|^dBic#tIU-|uLNg9Rv^JRzmDak(2*Sle6&aaLV{epR0`X|LU}1Jvm3R4%Gj2+8J_rnj=qUO|YZm2 zY*pghE$h0^S+Nxy9l5#_FvR;AvQHDU^{BsLcnyD|qccC#re@KfS(9#xN!7FK@#?1p zbyUypo#aaTBu&&vrg(Xq@`KYYa1XOCc5IXs>yqiX@hF}HJWLeGlgFPYU&-I&Bg4x` zy{YIUK>vhTJHeltXcVgBAdDCn$suU#1IGT8CLklY6g$S{{vlnr9mMA`#z{<2GNsHk zH}BK|cgbkoD=G&;LHNh(mn#JSv=cnx!;kG!9S3LPH$$y?1%>XiGcRBFJS4YB&g$q? zqpXeinnW;#&$|XJuT?oB1gDs9A3gC}0m5h#Lx-|~RMx@)N1$lLQ9)m?nyCD2M?CXI ztM*>dkPqDdYRc8>e`(5sl#MK^58A2I8h^#YCmZ71RC*ERqhs6e z`gu+XYkks(H!U@*##^(#G--)8=&s#tm|>T7;j_)VpA!O9>Qh6Jn+Ne)*lTQ%WKNTf z`>Nf)b9Se{5?JZd(E)E9g}>MYf89GH2?%t4my(Dh^kO#J!2^1JQ9)1zUUw5g`&$E2 z>1$ZA9W2j>XXx`izsW}a$yPFY<9&R^x-;(iR>O=webx82UjMmst{^Cd1aq3^;}j$$ zXYi{cL_7wFN}#+Fz~j2!5!gM3gZv(L+zE>vkc7*mf58mYH&@qEIr{8ug3)+hkhjH} zna7x*V!vt%Q(-L?H+L}WhfC>M>EK&-4v#s19-bs)$wfN(>oG2wk-Oxbo%?5_Ky`^T^`J}XKjr)NzKs<`4a>Q1NaY1w6% zC}kk2W^~>fvPm9xf!)K{OxJ*+Lcal0e5n|7^aB&p?sNj9M>kuiCHBkov_Gla_ALyS zsXBoD#}O#?W(l}DClDFx*5qim5~|9?+HvQi6Bx?@BYR{@R9FAD#1^>{`b@*iSIq-$f zpqG)C%5m3lIoPzjGS%9E4IK{n$?I4e0tHkQqVZZ^@&zk#Fl{g#d#Tzs*N2zmyskmW zG>cdktPp!jL;9({Qb_Ix^H*eIGLlsZgUuPMi?5~6Q4qg+o4f}||B$8M=S&K;E1nkU zdmP((@IfToFN4Yz8!ow~eV~LHHAVf@6itb2?*E#ikX%7$&vF7d4udL^7$hX;8C3a9#iC;>_70w};>7sMN#BfEe!9Rgh z9+-O7ikUZ(_QsV!BV?hV+6B#OpYU3C$`ufU{VT{{!*)Bz^MyC1gA8qY47y#vjyA1( zhsItowTF+q&r8GE(F(_$T*sTmmjR`#Tous^RFRtr%fh_q+F;zGj*86|r(XoWtYEp> z{YhO_TiV}Y;Y{#_yCRS6H|y9Ne-no+)J|B)T&PRx^ZZJ^P3IE>)iNV<`!`12CWH;n z&@H|7yvFr1y}#5%pu=@!iQ%(Z_7lxok{i4B2NnW9UJ3(PM(=0@M>rpJxH2$B82c;L zB5)@4@BEn3dLpftewpH%`ocKoljdgdqCv(wd5DCJ zc*-sVL024}woIV&Qp*4ZIJ8shf{cH|*7i&a6Y?|a}L85(k z>;sW&$}lRDoX>(rs$e_BPex%Uc*eh%qQAKy^h>nC7hgt05YqUA(;W;8Y3FG>3w%-+ zAz9b1&}4^EAPnkg1+{@j+II%qHN20I*>%)d=?9A?p5CO=-o)pa5AZGZc?g->HdNA} zc238u7^gP`O76Y9L|yUFV#Q=cgPe_Hg#x)37D)`(?{U9G#==>KfojATf>ec$13anb zp-AppyfL>jihP}GR3nY2Whe^0_$93lsb*f%5@?P3d-M2vF%bZ9tbSWIx)bRywJNe~ zGLIW3GR?^F^}cf^vZX}HHCifEiWqTqlU%s0t$`1mG#X1$K%F36U14VkXOL^mXwi8y zWl{t)tQ5-#DR+xh%B4B!jkU5swd|P2rGPnxMbpLauC9H8I}b%;q+aR@KO{;==as)U zT!rdAzUii(2K0b!?3|CmY0_nsOS*T-se{G}ZltT{bo8`ElSwmMc!CxM>EL9CXqgL> zOoy8>6xdUtTDD7XyrF&u+}6|&Sfnh6EndRnyTT$^o*uYAdNWp$tY%MJg$RMt+eoYM z)GqR81SI$RhQApYkujPv>@i~)P2@|NgJ$_8vbI=k6%xF1ZV=;iRNXw{O< z3C*dzUUIcXB~y%38u)CWpq4_!R;z`$RbF;jy84~BUirvISQRwvkmvctZwei{{$sTD zSSm$A6R7}Rd}szk*qogwpPfGIwSF*uQ28O~Qu)%jK7hoWY3E3}b?d{kb%(@8AX_G5 zM@=g9(1P>OA1kVQUTT8QAH!K@PHIVpf$8rco9#zQ=+}e)g8gH1U^TR1(>A~4ZuDC{ zoI+&gSg-9ypP9(jJ(HnbsD!`Vf+S_q7&kc&pFRk{|J&Uy=$XBgPvN|tq@=Y;NNKxi zTDE0aj%rF)s9=L0SlN!Sl~79m&7M+_s=!a%=F^8xi(COXz$K(JDyTR~{C{knV{jl* zx31%4;$-59ZQHgdw#|vzv2ELSCY;!|F|lpT&H2vxacfwUy4{e>dYCc)fZ?4Q7nkL!b z(QtHou!cwdP?x`74KvYYwX{ez+syDr?`0V?DUM~8eFiIobWYI4x5n5CAU)MllMHU1 zfUZbyGer#!^%0d;u{E`%;PA>Vxe}uqprXl9pvEr{5Mwqr?bXVRM&b*m#o=hxAw3bk zpLY9r$&>E$DWxcM`SuIn3oU!;fLPvERZV3O^=ICwnO2_enQ2cz1g-qy@(2 ztGRO-0n#n650!$L!mKJ`ZT*(>xi|-#6F6dV$N(p}Vx2haq$h*c?1`t4fOdy?9I1^| zo(RzWvC)mUvC3q2&0TPUanq>w9A4o}$~Eg^u>zZphe*A0PrP=l;t3oq+){WwYyY&2 z(ag10_5jQ9= zTjbyJ4B0)ldn0c5TkBo^@F}h$*cQ$uvX6x_UN_eJTABKVe}*Y*4&Pd^j6v}?SW|{7 zj{m)Za-3qk!Ip!mvFe+NBFKma2zb^5wf(Gfw(JCg#5m%Th}EzH((4uLQ|g=80uYSv zfVaF_p|Qt}lL8N&Xb2keff}k%)oah6f@ft%bBwiKl%dKhTMWi``mu_mtbZcmsx4s~ z?rhom(}%93ffC$@e|KHS zr|oz1&f!8=oBh`Ql}V}5fkx+T$7V!qWGzR1pv{xPkCB!HoC)JZ1qI~I69@l52(eCT zn;N($dOHjBMHe3>kwCU@pIf~!;s6pwF1)AbP#AY>`gsZZQuhf{u)w>^E?xmM?H~5a z9HDKajGYHSAoB=;E`=juMtot0jy32>ii=cE zx(K@;!@_LUG~78|fByVC`D1Wn-1Pq|K-mZx2@~&GiCdR-LE#a=*#FyDX@#{0El2)u zHD$#M)ci|x`H#ucY8ndqjPxHjB_I{F=6~cB)lyJ;#Kdb>^49WZP+*gWoC35OnAE+q6%Fg+}i`~s^-B`TV_>ikRnpb8oL;Qy4E*N;yDQ;^* zO#g!}=3h+s3N~ad;&I|H*R|7k4H9#bHs5Go6f>}9lyWcUCoAYN&h|{>zuVktk7!?e zkC~;kD7L1pVXUQ8*yh`}-34w_w`#6voo}=}pmId=E}sG=p|sDF79=4g>jyH~)($R=>~TFBvKMy z`?aftGnouX>~fG~Y>{H&@4obr@#)`N99U?}ciDweVv4HWQ;HgJwXU_!xequI=!-=C zki*zBlT2N)4SW#p&&;5PDK4p2!6{Fdi2qJpo%30qEa6^0ramuqq^92@Zf+X1Vo`t= z!>ZX)diFawXMZs>G&iL!IP*uV==9*2g0tG5{vjFQF{s8lTZrJx-L;LvP3wx($oH%K z2qDF@=x$hjKeGEC9z}Gf^5~`eA>=*qC_RQ1JR7yszMT}0*^&U>fn0ZO+P!|q#a^7h zH@2}xKa*eRpPSF|wcqW{t}3|A?FsktgXB}tjg;m4G=;iPpm*up|-+u5Gsb?9!^jZd9TTvWG^xS+N@ z)Sl`L7l%oP9#y@{H;R(qb^|=R%>Vnn%B`xcqXctp2is}LyC@G?w4pd6N&PXtmi*}i zp+|8EN2;hYCIO|}h#nn_nJ}X2vIT?oGDkG9XR^oDZJN#EDojraI&GQcwprasf5P}I za#IMoMk~Y@W)x&3H$A#U`VefQ`)h&itLMc%K^=6>Lg> zxg%;ZOu8GQ)n3#F;+qPVDy%npIrFNC-wqcA#Mmu61GkDb7oD2sjON*LEHt|mUG(Dt z#4O(Xa;g(PDZl#1>o(*q| zp`jlpzN7G1<+aZw(t}QSzH93&Q!4?WU%v(oiJ}XaR~|o((_HeH^MRDonDdc))b?I? zz|o8m*;}}^yLzY&Z{aL1&98nuwzP0Tbrr%nyNQe_wA zz8Bs)7$9_!U^L}1`wnzP+r?<=NMWMtpM=A7lciZeL0RjK(`ia}JLU)(v zZF8yIzoYqoIo+H-k1i_;zTv+R>Co|H8Lyt(e@3D@YolwXVDXh+n#2z+3-B5ev&nrOI7=q- z4?NB2B5SR&^8@c9)+*e@>T7FbI3M5ban0#ut`+yyd9_Lu38M8yJe3=guw`L?Db^Gv z3D)NRg%|ZuQfTIQg^$eB z*uAxl826cbLTX0mBTyXc()(aTvpX4VvONUcTta019Z78gq5?Uc{2WzRv~-jRdKBnQ zu|HVnpSDyXo5}JoO|SRHx#>o}Lx;d|_zR}}=w)V1lF?y;AUC!yZZFcw_u%b_nSHUK zu}~OF!3FItC3>FdSw_ih40trLMZl~%Po!OlCWXAXsvO#kuP0-3j%Q6@7<-B5Gy+Xo z&2eVDMIT@T$h@6Jk{)|e($AO?594dExnRxjuPNL?Vn)bQ92sMMg4N_s7`i2~L=vwq zXjmEl)c6yPuaCW>mHvb>(ktPc_mT>h*`xBCJbBA|6q3&JM5H zV}?RRiwon1FFi7SfD*Ax2Rj6ZBpqNl)}IJh$HbBWE<5GH8qbI>k!LV@xCL>+ct&%0 z{{7=1b&4F==ru#Oe5c%*xF0k1J)19;*MSwH zbDT2`kkjp*-S_XEC&(DH8UY|BQ;w8IehP(v) zGSm3|J*90YRi%txp^Ht{3sxik#}mD1pu8YLfSY4`q%6KWJdy z@@(2ymS0ju?SStT0!M3Heze^2%Mai6Zw0s6KM9Taz;7lR#CN|eZhpeq<^52FMG_GO ztEc_?vEHEqD_@JU3aQQ&k&k=_dovAVMciOXy@cF7xW4Bcp%A&X6*EB4ruF23Ek#5I z=zMZRAr4H}tNcir<|Xz+3FtD#9RO!Vw)JrdV)>|A%BOznufb^!wF2!`sQsVQYNrKmQoxYAg;tqakwD?LbBLX zGIiV0nab}i;k(mp(I-_c>bAQO>cRRCFdIP)C5^0qh@L7bSBC4N0>NJ>Zlkp=>=*E@ ztQhExcR)6pF2$8FqoM+YCeJms^FeHtLC$?EGi2b5nKNUJ;?SGk&YE~gewNiBUJ@^$`lvM6$Z{#AY)GOR0z$mvT{qul4jhR+YmjhhG#+YH(N;))4Z9 zx@R=*sxU}OOW0L4IbD|Iom7p<(NpWCj&DBw7k=ZVOMFXJK!btS6^ktbB)4PU^QDk6 z=N6PlN7jv zCNyXUzYfxymCw*NSR5!<}jD93kj;Jf|e-as?-km zDoL9DXFv^V6$)A_j4)>GMddohHM2rr^{VoLNL4(~Vi4=KHIKazZ)dr0N@PXGXsEB% z;czYuvx#CG{~OJk1~w>Cf(WBK6^+Goo@OwSiwlZPp|b2NV4$QV%>y+#re}yxqSU!W zpq7v+mS_4k0+AEDYGqO|81Ga(>NRV#Kl!VDks+TCl9NZJ)I}CdS~W~F zC_Bp|?P)o*t%6b95R6@K9EpS+kTMj`4Q)$q!6e@o9OF!3%x+`cg}i9HPM8rs+iOY? z71Mxqsi+1B`IdE!CZ%y{I&|iwSy;E0!n~nON5{G_glSBr4qDFZtMmBOHxMu@7HLTB zgdu00?F}I>m`RLQ7h9$!*s9mhGqB~p?#V)@yZV>4kL*yem6l~)z#}|_n5>)?4^E+K z8+L4akaI2*B|WU?g>O<Vi`*%_dQ8>9C8EOT-YJ&&WE$C%NbwOS{{ zwdJ>C^U-at?!?or?HA@tm5!{c4(uSr--dCsEFd~^+S9E?&W$RIcoAMda-Fb?e3@!h zIp#X>2)3S{D`U!3w+-!-l*jD6pG959yNkdz8AvtM+qDS)`iMhUYOC)Zpq`<@t#<4Z z>jKbV6G7;~yM#FfH|&%c$Vk@GOt4SU`#|>}aUbW-QM!$>&mnJa$v0$rg_og-DjgWY zmN`K6#o@Oc438kYbH3-Kvc+Pr*ZYUed(QJV^g`BpH&v}Bu-NY8&(+%%TGT*m3w!G= zpEuG_s}oBn%Ag#Y@-Yzc*K(18-;%tAkpL&(k5#Sozvj49h_$Ruh31nNHJ3}eMlamy zlq{)&^W7rR{gJis;ePX+XRNI>Q!y>ap(f`@QS^eN0She*UhGE5#k)49j+QOatCb0x zUO~kcym_mz6Du9H)_p@=gI+;3xA8 z!m6O|9@dlBaKv7!d3XDE({?LE(>4Sp)xpL=_bzGsHE>QfH|U#-Ww$mcX$`+OUE|}v zJa7fyb~yek5=rviN2*(<_7!0mxa(B^cQwv%|MKv}!4aGL`E+u>d=?s;!{@4=dU?8< zcs7iQE1$Y7@LuQka&o%5M=%8BZuHDj?5$^0%4-x{HJlDw*0Q4$siFU|PB*oF$_G0v zv>GBVl8Hgct*8zwh`7vD^BAZ{j|Y_OJX0_Uw2L->U~g1&%MHYGu2qi%%cu)wOSb1| zl;$1cPy}BxP-UZf`OMQ>Qg}cp$5(fI4w z_7iZd!`AWr;@W4wIj%$Cj~BFbYf&KJ^w6U?)%=~ zlUN3i`GI;@5W9*+WaoVJC2gYS4{VYT+rqak4_!HaQFJMjA3xjx%?&pos<^Fz_^)T+ z;!B*_m95Bej(brDmF0cL6)w*utZ|>v)2wNJx;4{3;)=Oa*qXfGq3)FBSz?Kszl|^C zvQfFLEv>C3f_HHa1;3lScDynTg1){y_04QsH3obf{JlMV9j(huU5@>{z3xz_iBKuA z-D0~~KdU6Clt{?|q+f%dQ``_#n`sriqAEd3RxTdxh0B{(4YW;uH@`W-5+G>&?jObD zpYEA2h!#oku8^ikNT~_wI)7*(ukS3Acq@k8m+KhCTD4uyrGojrYU7nqyxLZ_T39DZ z{UoM)_^j}3r4iOA0 z_Uw+QG(H}Z%2>NiVrR0gbiWy60pKSsu0N%9`hdftC2hhsH{wtGAGi02sYQpNT&=fH zpfd=G!XD(U$Bu_E=!DNdM7GTX!hHDE_-oiIx#82IaqL(Y6p+JQ65-tD_Q_ z5j^OBI1m}Y#QuLL;p$A(fE_Sk@Xoo%+$JsOlY9x^)}cTb!l zNrj^phc(R~{6c_jVIe_7l~URt&MliKpzvaTp3X_&Db{GwkzWROg14`a?Dwvq26ycV zz&;WAvp$bn10#XYi~Cgj(tqy^0B3TwD5k=8ckX#g6kl6V;N8jk*}v@Q@F}df2*7@b z)XNd77IbI&0d?vkWw17cUOz`ins-O}c-6cO`GRcuETtP%;79K80%x8=Xe!?nrh_Ta z@&2#%AOFY0`t%N>wCJb7;q)*Zi~(COcv zXgQ)83cnZezu~(ct$WW< z9-ZOU$kc4Vu7P9jGplk|CXx4@3iwWgbgrIJOM%ITcoX92YsFhd{1T-{qRk<*_JpZG zn8SvoJ5K)a+>WG10KZPlQqMAngyZHf?ozi($ssy-=0#a>&n;?4jj0Hd=7_Ua?WEB) z*iACQ3G7Iar79%LMJMkvlK#IE*OKxwa@B8s%goYtHa~cs>1KInipShB=j5tgIzv6* zV&UnP@}pk+@%Q$+gJmJx19!En{3pq2)JRlXF=`1Y%>Fw>7i zHcJlupxlwc2PG=3H9m+kZN>CuTg9HdZ$l`uKtMmJ>xF_3)&ya_6l`=w#dc01H>yKUDo#`CntS zPZP-wA8)-iU+Qvu8Ii~t#NDmgPwXa4;dfb@3YBA64=nOh>~)=`(QRc6%7?Bz&WA3& z(IsD}i||Zd*NbSAAz`m9sIEwOanj;!y1)i5)fd%OjW}E#Yuj@~=ILeoc(hO{-YwC- zdP>yUe5hp`49*IHhhe+0#hFcNmf$$FzAiAeS%hXWp+ITB^D|58Acqlwqon0$ksBW> zDfFnu0-#s~16OSFd5S^8agsRC%ERucs+L>D(S?d+86vakIwYbfy8Uzbb7l?t8?DPn z^}}D$qLB$A7WLv{A^q@xpclnm$Sj&+rar<(8=H0YKqirB3^DE ztQ;u~-+fIb8g~;Z4cHde=AVv1A!yhf*FCe3fL?1VHWS4ybAnlXWfhEwf>hfNVxb+@ zA0fm7O2`C;YaIc{Hk(lFiiz|bxNrNmTUq+k{RJlqF?1qM=FgMVF~sl+GZ!)hvk2=Z z&Ry@lk26-z`Q74(G3c_7>KZod!Ix@6z+BLHU# z04@}Vbkm1Mt=?GhWCl^~4oz&_zK3l^=Cu>tXw9U-hFR98nrDFDoIz4CTAujuG75x3 zliXa;@8s1fSn1&rSsS5E9l z+n< zMoX(SZg|;hC*E8~!kB{ExDLem;JU#Z*64scN;-24HZyP z^c$0SRRR);VfQ(}$2sdS6a)c^wGI1k4o9fdlZ0;V_@RSuPsMmRQ`a2~5jjj{(`E2* zt2{lke^=b(>tf2Sv#4UJlaVp9${*dC;aDo%DH6M*l<2}9Uddoq5Nk_NuYL8T+kQ~uuC{)9NRGN z1e(VT_Ch?EpJHXkv%+p!en*)%cV;}(Siv_rgLNo8u3qNos_pvt^`hI~gje~AHt~=K z`xcc_I6;l8Q!ClHHp0J5Eg5}=B_9Tkq(;`J@uQY`vh(NI33SKwb*gTFJ`{e65>56p z6_kZVg8j?YZTPg{`V?{u5D4vIQ6UpW8&Xt0S7XGcQLSV;`E6F!Le-c8db})4YO6ES zI&5W=2$5{?tE{a_f;6zs$0}0FWPyY8)YY>i3cpa==$yuI=C&_PsKveV>;G)BO}6R zIceX_Kra_~n-fluO`c^rmP4?8Cm*!^tkl)N8^Qzh%~^FR;uAN6l*Ykru*oWQe-1Hk zJw)H!{2klS?1vfvg!-!W@U-uevPZTYdy-as=>8GLqY>vIKhfNdF4?u2B(o|rf(Py7 zJE^T7ouQndOk3e(6A5<4~5#3V0#Yu=KDcJDC2Z0w1#FHvF`x*$WxYifTTg{ek z2@xD4ad-5jH{zc@ibW2cz?O%wYDtEe@e&>J*!+xDp*uYUZafekitv%RXtNF&$%4$@ zOQzvuSAD2P_8_`Fy14T1QiT^9_4g%qoU+&6223mC|=&tB>e`I5~y&WsGS`MoR zgr%U00>-AznEA8zdlp8Ep_b1XRUa758^a$k0$8aWwP~lLwth%XSFhk+5TV#gn9CdBmNt2%JaELOPnpC%{K7 zU13>rv<_b)^iFGO6+~NLhwl2u@9$b3KBE!4~(ReIp(!wR4b_ zRpV)TX^mYL-51cK7Of+ltI8$DO@`!2JHao&KMSKjxj1`$*ihJ@??%*CyK#~8fI$G( zc;l2zw#`^|*Xo*R4!7VXpd{NVK{lNndR2>3w4JW4Sr0ZR@9>|ev|cGC z+NMbNM^=nHKcxSXdX`l{_kG+gGb%BlR{WLC6Y+DjI%?I`LJ4d%IjwoSTaSyK^unz6 zVJ&B_#gxi_DIO0jEk!n458vR;Yyd1~CM{M}mp(gH1q(@^c$}TDOLhKo`jInb%O(lm zJvKjn3)L}U2;K4LaCgdDJSa#HR&TkYo!$OfSuAoM<$RoU4+56!x)#cvfnaCbO*H^!Xx zp-wT|e~&YKZ?#(^ykvG^cJ9jI1!sAOg1BqZE+_3WZLtTxDxVKQa!8#m!6@b1zhGTI z#&Mw#a@-a?s$j3-66s^@Od$twR8(z8=o&h+ie$)vxi(}YH{*rA*Q^l;qwCC-wiUyx zg#QCEB?<$BY=FU!F6r4?j|=&@%WF3(0xwZoD8<4LP-6GyhS zVIUq|?g#0gz_h1xysJv2CmzT3O)B)=Jue2}C9gL!c8!Mi0kK=AZAQdG>i346hxYN^ zTm1KI#-inF@IYLl?+3c9jyhgF4%;169_uSAS9goSD#jyPKg$~w*PnuB`_0~K=viLw z!+%ql&;6}GSd8h87!}9>;!8vK7Bs&)2d@sJOD5$L5`tzl*!=!fkLj41+7-s=YdJXc zI>(`NAAy$b#3Rs6m02KxbPqdeYM})ll&@Q|7b4q@u-D+F7C@=T1iIXKjcP4S_r|;|b{8fNEc(K@Rf1guT0?M4RLpUv0h5qFX zsl(NYd76^_Z6SX;vlf!G4`E(Wo)Jk(E@(rhf`*0SmQniye9N9rtYBc^aZe7*)GvoA zafMZsf$yU7{3n3)a{aHUK%>f>nrnJ>Y-~qPk64WVa3LFdQ77g0V=FHA?q4UTb6QDK zGXGd}-(Bf4w(>QU1K{>6PY)sGv-#25orY`cHT^2stK;?!c!!gyQ49QkG?&yFT`($u zi;L@jdnXro<94KN7df?M1P;|(NIe!1pb3zV?7bTpUe`_QiTyTltGKssL}W^})YDNN zwyNs2^=Bva&CUM%M(nRcTc>jd$}mZj{g1pZuxQnx3HlsH=DNtMBip zbJb!0J@bBC?A^}`?C%5S_Ce|V{BqtcQ3`#+rW`)r?+-alBv^Z~*B(X_w}A$VHJRb z@Be&@-1}!^cEaWJ7U$a*DOVCZGZ#v5a-Dfqf0BB#yuGuBqW6LQx#WN>Jf$a>F{x)x z9to55h0lCIXG8Dxg)gX?mKsz4Rx9Zl__9GLx~lp9otR9?R3FNf++hXp1?OTL(h${^ zq*GEW&HcQ4IjIGPp%E9J-?t^v`Mnqf^MKe`dhrF_pND?yrTvYMB5j+iUW zOVF>K?Am}N#qAXgbY%hk3g!SZj+nT^t;GHLNU7TsJmu;K7S#(A{LnlK?A*2_LKXDY zvNCdA`Y--YO+?PbQW`B%C1fmAEGs~50V5e4)(mpvrk5%#_=7Elsya0xBS=LfF>VYd zXU;R=cTXHFwX!k_Ytjvo=)Af9C%h$3gzdh8<^xCPZN{_eiR&n33Qak8IKHt&X4V9u{ElKRf$I4cmazL<6N|-BRGm3S+kuYjBZ9u>)-Z0D zjS=ww8++r0rE{-U$&Uxa$A~ENVVTIehxN9|&AJnA$UKkhZbE#HMKcBw^O5BBfRI|< zg`8W5%LMD<^G@?;A459u7VE8o*XKF8F^ExZHsuv`%<&mbC-Fy)p7er+xS%AHlf!j5 z?iZ}ZpEtK5WV5Bd4V?)-&@hdjfO?Jy-vDadCwACIh|?`Lc2b|QhcD#08R>hSyF~ct z*qW0xh(e?bOuo~_B79s0xju;w&oCp}p?TrP=gV;^W$%(VMb~wQ1k@T9{T--wMyySe zPmm}Z^gfJd%iqH#uPq3WOq@w0f&(B@;ucMk7ZSNtqg%kt&1>CJen-hD8uAN}i7RM~ zAC@yh5-ZqP!g<#J5>%j&GbDBr4>xsXA94${!RmU`scLwiSaydygr=g=HH!Q#;j(#l z8Wbp_B%YmAt6i9x%Ca``U;yDUA5M_~hBmg?P-ziVX+Zz9jd=JAn^a-;3<=2THsleC z?_dN;XGSu_UFMVwZDR+;+d?0%`hJyg;-tu@`D3D3;%7sduQD>|{1GW3*gN01Fx#Dt z46W#M(3}j^24f^)d@B4CZL{GSV;BMwTz{f`(d|;EZzcE%%UQ?e^N(%uqVYX##0WI? zUSS2+KZ40!DFnBk`5C~mMHN(>2xFD2k46=wKZjB; z3jrdg;rcK;If2z)zTJLCk-}-D9)pCwaNzDINY}{HsJ)!oEz52>Q4$ zkf5qET8IIC%NK~O-Ur}{wx<0emY*CHhUyNsR9xfIs@vTV?X zrEOX&H!-b}x>3Wxr$5gRE3gw}Lkq06)l{^B-waCM706>T>Z+k#@Y;OrHsai(bf5?p zN7YKpoEde&vRsfwlWRlAXBf-g?Nn}qWQn@^?quNF$$223V>i780a`7jca)N*-xi3Iz!!lnymDV|E6^IVz!y6%KkyvFVYkMTBQ@a zD2|gK?B6%*{XM#lh_6B1!l36xFA`Nn*_TunF$FRP+x8p8WEqj2=}+KT9O)|~uwXzq>|RmmP^}-Cj?AOh zHDta~xz)nVZ^(3NJ0pG8V9_p?aui9o&TBdCNU9QVnq)907YVm={UjE%!zSJt|CoQm1e` zVJeVOhu7~UhR_l`(BnHO9fuY0*uea^XhOz^!C-TcIKFAd|Aa@ellI8c@i(}ml;bZl znRmRO6O@^CkIEcJV7oh}AM|*9x=HYG0n1{kkTPZt#i3C8-F;xD$sNPOp~roVn$Hk6 z+AUBJgF;$__t^EJPN0&HCU8Amj-f1|*G9k)?Pd&!CBoxD#*q!@uWMr#X$NGqjprF5 z2g~OJ9Sh2HM=onU11?Vkg?g-{&5qg$P_4*#ae{}Fay|E$EgemVZcv$3&blN_oGT1o z&M3UE+%(Fm95D|Ike|PH*t_HQ>y=cfdPKCql+2A?UQkHQ_6Cqz;Noe=pjeiW3D|2Oj5Cj; zW)|QeP>%C8L5^kyT4C!^XoJYMtBx*RROX9_{<8id37#@*itnw#1~lK(m%8{^!= z)cmF|-3rTlBTV>f-|F^rOMQHrhLHd^=j#K4$R+V;JGt8vC2#&+`FzCwpnHeQA`seaZ zEz2xn5xb5Uzc>QJ^w@izd^(`r=0b>EEuk-H#CvcETf+RCeo9`6;?M#%frKJ>R!yfk8yZAZcn$Y&y-yTDj>uNm*TFh_xs5HX zS>J*|=`r9;FO$613_t%0S%3dF2!pRv1O+@0hV@on)`=~l)1f1;POuKU>ZzvTJ3xE8dOC;Ry{B@Q z<9Dv8VLSJq`U_G7<+8-TLK5~J_160sGf=(9Hb>u+AG_#6Xpf1ALr<+Chlv$DTI^%x*7nN_7W_HeiJiiXpc7)q@Ghf8H$H^p&zUcswr48eavg7s!{ z)!lY^Rd}hle*uXDez;_1$lh^umh{+^-j_Lh9?9}r)R@BO8HdW?p*O)na9sjNf=+N# z{#)f@k6T+_UYJtMbl#STDXh!ulnP^Y*8VU#H5EY%QF#2g#1+bb-iCNr^dI>R5Np=r1RTb-T4I z2SbFr0cka#w+EqaS3VE1LlH6}DX_?#b;E!aVIES_Ti!c32I=PLyWtaD&4deTqlz{LT zj;;vY4Nb}OipJq=$#Zl^tnTVZglB^W9}SJ210WLL!U?y%7E^#xSy#L(&TjYhFRj-h zGy2EsgOx#Bar6&XxK`nU$|z`Gk~cpsG)sS43hdv{Qmq(z85&Niq|;EJ`5PBRg*J6qEDPl5Ml7?q?#!E`CT1JGI6w6U*Zw72)x+2esg#eJ=%NR zJ_0kg=$ldZO-@ZkU`-f_pU-E;#dAaqXG<&MfvY2_IFfd*7(Q@eD=*?omQ309{)>+H zT`hC|xl`}e^|eOsG2z;#`W^kq5K#%O9bALzJa{%`uV7myq~;Wq7ZnIRr1XE9{W@z( z_Tr(3i=EcBK%j=iEiq-`{ICF9Vdx(8Awc}()DKaa;}c?6X7%BR<8}%XgTD6&CcF)~ z(Thd!q)8Ng?56C{Z)mDip1AskwUyBoc5~c+Bi^i^Mce9eMbVR}{R>s+Z_R~p?GDg~ z8Ew7ADE-E$7q)cKO&WtETB}@A@l)QA3kZ;&KNhljX+21 z61{T3(&nV9tN1`+azfi-#YNq@QDc%knFSBFMe-a`BVG!4qWb(_bGRPC!lVXs9*~;z zf$J(nElOx^A23m?Z@t!>K22Sfwp=!~T*YzE^*m*VeN6?E19YY6F2Ct(y0C!*xnWK3 zs!EY%&Q?Vm$80#;##46_LI`J8QJ^#tTK`zMyt9`6YPC%-{^`^&mh^5>*EKDN?))Vc zEuf_;#wTK!e=eWq<+8I<>qVPjQN;;&FZF@=DihH$B+Nyq|N7@QxEopqOWG%qs!NWFtSJwJ#EUXVDd-`3l9{W1M#yR;USsPM^MnnPdIKcd zqJ7%1yeVd272+k|6If^^z*Vp`6;KYj^gteL>;-d_Q_p5l`y4zHKKx;1LTB(*V4Ij2 zAz2{WW3u9224);aa{2k+JsE-^iT1$(UxHwVU(jIVNqy6X=9hPL;i&-; zZf-j|-UgUumpO1%8!syKC(?uMuKJln(9%vCRrP#}=f1Qz#7FXWlz zF$A<2ClGfTK?R(Tdj4Quqy6q|eddsSEi?tQrhL=Not}~EB)aw``ou$|NrT7rrcROq z7Rp4yb;F`D=gu!ph#4&cHW}68cV6o{3XKv8=KUoi#Rpe{JXt1~J5AD_{4)t(NylCw zP}m35UuR+~>D>sIq}>n#uqX!WA>}fIJTi!%*OS|kOiA^mU$8^tKIaH5?*_2Px_y1` z9^-5$ZCuhWRnort_8q~XE-1>)Neo^A&n)~>By|xIdC@?dfQJNR1B*%cvBUJ^xMaC7 z*=X?KFXdhE@sEkRnJzwia>!IWw*m2O`67K^0d{bAoFN>7P4BdODReYB%OuLkoowjx zR211a5X-!c85Bc^3<}5gr5bd+=lGNKk%F(FzA^(@R6<%K)MzxhM`IZ#3Ytb>@R8-$ zse>NS?0a$$_nJJM`1d^7sH#Rv(_3oKoQ9817LdKEf7U^p-ID#6S;qM>OfOv$4G`iZ(*+yeeiz#{>=XQ!OsO4nx>)!K`RKO<>nD(t*!YMxHdPxm`dm~@{jpf zYj|V&*Un)Qp8jtaae^J#VtRf6_Ph4pH0iZ?-kd*oM01L07KkZY{oGe7BEP`rLtUz5 z^z=VDm3Su@zuW64=M(gR(2Vg^?{GK? zyU`CpY!P(cWizpG++e?m3=t^7ycwI3XUXeLtRq7wM#zfjb8y;*3iHa%>D6FM5 z*$)ngUVe-S89dEy=czon^6tTT{(X}h82xh>%VBl@2!;Owc~Cd7inCuHuOCAqLey7P zWOTP~-uRYAtBAvm3AnmRuMf=^IXi<_+{0_OI%-VHn+q6vX347i*i=#y=!QFjiFTwC zY`Xc3K-ctKmYiD^P_b~YDN_9`(lUcDcpDW}$P|Us98<6rXptAV^rt8tn`zwk7s5WG zBlg)p%$VwHsPz&0+9>6bC;fMRo~HqGQ*k(&-7W(SEA6F50WgHO2S_NZrVHeQJv*Q> zcQUYNyJOoO!K06&$v`Oa9x z9=j88410Z{-%?rX{iXueo0O7N!*oQy6E(4K0Uc;5jb%fd!=s~*O*^eqAwiU&;o>yc z7=@8Qwjjfnvi-mceU%Y<$MRAcg;-O!1P+)*f|XAwG>5XDQx}N!^o?N9m0-KrIGWQ>#=rpe z_0#{MOvGcbYQ+EPHWOgi6J@6lIl|UBa3!@bgE}+EA~py4CX@QnfH)U&Agl zx02@EMzAW4eE2$W8>?VI3ARd>t>Xu<1dXkX?Y)mEAq}=d*x&D2{8K4Yd>oxz^(ECL zprd0rU-_>pCn7;)t~EtWU6{x>%nZ|;OgrcIOh;g!bX8!1?95>%2`?S5+V2*>z#;!} zc8{1F<7+yuW5&CZ{b!DE%OJbTO&ML6C!^LSIMVJ~H)~yts0c0lO2BW=9D5HSkZ7>| z+L|&sHQ6|ZHxP;rRbL0?a9v(Dp2w%8X=%tHhatAbmeP_?J8VN1wiu9lvF%wsB1gxL zdliw1uKqAz6?HV`)?`2i8=66gx_ZMMqqu1`{2%y~97d&LqYAS4f)ZKycwHK?zIG29 z<=2T!>>uRI3Euw@0;;rHvsX9(6MZggP!=5ooqF=+xAzd|hS|{Z0ea)WuEz8fOd};f zLon1~mw1-kv!ns;Sd7GIE5-+f=!&P`(C-)gdUz1plI>*(dA(j0`9z zr{fNLS&EuD*3uvN|J2j~I@gNh&ofyC64?(~BbLzBKS(@6OD6@be+CwqTVo|7k)^41 z>;(ErpbvR^DWQ&6GSEwcWH#@@)f^oTAwQ#=jfOKG+Rn|(5Nojqk4QL z4`2~gNjR(W4i1UCmT99BMyR#&55o{h4Cu2y}#{$Wih>JkHaW5Zi3&QPu84MzQK zK$iiJ&Xp-}p{hH&Ezzz`;5S5I4vc>zE90E6HdmGoX_m#Hds6(q)(-(>bP{A5Jr1YQ zyq6fRKGpioG7oh%J2Clm8qyzKq}@&J`mC^D)}sUtDpOMcWrXn;H|Bhh#f3^5c$$tO zluBjyRRcRF^JOLfa+RB8woNVt>Hj8}ERTa-M-}QJ9OMceX{qf$(FkMEs7i|zDITgR zYxpBMJFY2{Z(iMsDZRSyXAD3{&a~OSHk%E2Tl(Cs$u~5ksqbeOHmbfaJ8ai){6O{r zEX8&9Q$kdLa}eJ;?Ui>VAI{{+V)(IDv^cIe;tZmK0QQ;ft8Tt`3R>x3^n=CA-+PeHkefat{lbYoEnn4m-uM?Fv~2n1rR9!VwO z&pl*{XWnz95=U|L5h`PfVjGc;OtDG!s5abrGG)UX*KvToSo+m(U)Ptjt?E$)%GroQ zDpJs0S_h{nM^MA98h<`XxYEQnO-rA*{*qj?d?+|e`INNpH!<*(414-RTR9z1!~{xT z@=VeJkj*Rv93lz%v=%L{zQ35<=`1Y};3wT?r*Q)2rL)!T@wL;p%m`BBv2Yh2Rw#bq zmg?`W#x*7W;h{gF%g5WwRRe4g=+s-%V2L7VWLQPs8?B1iDZ8a>l^sZy9Ye7)d^9eX zc~!sm2_d3{Vb7?(nTLw4=zqX=+^@9uAoWTDY?8NPYvLMTLDk`JeCpQC-Bltp3|NcY zk4M(fCv#ZuDgr>~TOCwECj~)zQIN`b7MEyjMB(WH zXRSvI+TNAr_J(I{eJqs6i|0v_FYY%9(&hjlo3C+OuIYq{{f?}zdwcSQ8Dasd17L(g zvP&zP{e=%(x(9jD;&kJ|9`Xp65i@&$6&aX8Ayu`;#;zId^E7_#}Yiq&sRMb}p2W*fjwHcm6dLRdBN5Pn_eoJMAAcMre56m@K4NuwIm~$uo~0%UM5; zh4B7Qq5|d5%XFn6FpU3o2LZU)|1Sy9F~NpC!T6nkA&bz!-Qx}-J(ftoNWdP(*~~3? zs~#<~GYTE@+t>2K%Gx7g3AL__QSwyfbuqB2u5RIet0)ceTUB;m_SP!ais!4#)+UC~ zpDMJ!M(6wcAK~*GnH3;4b}T3K_sAtCPIu@AcDD!C`B? z017pJ{IxU}Zuj85wT$kY*UnH;ybEH1@R<__Oz2m+?I3x5Ef6aJ9S?CKg4|zWVbwcG zF6{hnWJCm?bOvc_$P3QBetVtz$WQC<1HfO%-3#ZRpErO1386$5a!1br-#yesD1>*>3SpQEV(2e)2DXY7d8kk*t>w{7%fPx8K)FWB_*$3Nfp}vY7 z63W0wK3X?F@+sYLlf7Rek_6t%scidNcdmjD__X`oE%w)mc#G2yFBt!)K<%0akVj+N zmw^sw!It1aI>&z~A}8l^ilwYmmW2{F;juK89fwY5_GrY1hj+o5*3Jn|%*ciK7D<&L zIkcVDxy3ytMtFe9!p8{2Doo5{M~=CA)o){oN$PL|dc7qq{T+ zfNofy;lAsPVfO{7P!iAtV?=8B<{KI-%rlBRut zBUXd_!4!(#J0yI3iM0H1*-d74#Bp|H_cp`$fzF}0tVF+&(QYdLY_v6JE>1BQ$9I18 z*0NjP=lQ${P~|ZpLo!xN$yuhIk3hr#6vIht-^%YNG>OGZ9vkWIHy@n^sPn4*F4(nb z|1e6jVUcSaZ}4T>lgCZa!jOz53z`DGBG3-s&HNfoO0#}Lsrps;jU zd??Pcp+5hDT>e?!bt2IS{O)~416*0MwbhpgTP5XPsV^TAO9?F9&tNCSg7}N-1ePhM zQj@YML7@hY_YE9=Cu7n)46RdpWP;=XNbOFTYlnMIR<6XMQ{533V0j=1{I~FIVNjFT zqSJ?fTg^E;l&HQ@U2|sj-(wg8q&&9aUc-^d!W>9Uj*)_kSWU)2-^N$3SSx|D&gBTb zx}-v7KeFfJIKn5aodsnHrWbcrrd*&jZ(nKWd8(gSqXZudR!8W>0`&Z{-nNY8p^9hSLn85)k7GxFc2Bk1uGBBnM3y4pnxA!B4f4Arn?I(k|T zZ?dAXnCXcB0J+3GsSEfOV6P-h2N`t2_46V2iM6~ZUZ^$x`WOY=)M=^b7f!T0s_~cU zD0^4h;bz96B2nB`KHchoLzI4`bi|V2LV|Olo`f0u*uQbrA=iH>gk=^{@XT3dW)@L{ zWYB_gQnZ*75B%=0nkL82$yaS4OjZFD4ATD9nDQG_0%n%DjHej|E0T6 z#RAKNp^h5*w7-@k<+aei(pY0W?|HSfgz?s~+~xk+aYfPtYTcEA?i;KYG`wEX3GwPB z6TWG6Wfq$uGVa-<`8k!x;QU)6RI>=@43Tp(wV1H6^-bh^+N!;PS< z59zPU9=x6Ve8|C1p`+P1o}VX1z{7ye39`<)K1+!WpFwU1gAjwz4;Yam^NZx`8@INq zO|xi`@e>{ZgF&07f=)lLPx4&O-9LGnFv))?l6|n=jFeLe8qUU^}D;hX{^?=l3ZQ?SpX zEawdL3$+@ijk=Ur16JBKZdzsvU7|uH%k?x+1?|FsTEu~HGqIflP*|jvB4P7>MFypY zDqZ45Ohn%xbu&gmG-G~vqPKFvDW#03cyq5hIr+_hsUmPPxU>cM0_Lju^T|HJOri6>@BCNA2pVl*6l|LWdRN6B zey6V(X_~3OUG#;^$4?JH8Ci+&5@q`FwB%%cJnHCnIA#zE?Tt8nvi;osj`sIqg_?o^ zSSc4|5GO?@SfD5WuhJTYYlxy&8LUF(92)>>KCHA`iG7j$d zyrWP(#sixYY3mfE*~>nm#0u~PhLU*}Y{L0r#$1JzJJB1MU_*b`=9^>cYus3qa@o6= zID65mrAHy9KiXDU+72Nt?9(AhzA&l)^wVT_%bI)(3##mQxTbGdT^QD1^)5KnE=qrI z=#26xUhI#x>*G=CC0Q_*-a6(+7wHhsrWFxtW-5!@@oh`LyO@PxWWz0W(^{$SQl?O* zD*03@mM9%fJ!)bP$@rPkqu%*-skLl1Y3R=Ubx*C!Sn1)$pfY#*RhKe)tkYl$kl*uK zK%#%~prVmIc?zGBtxJ?G?0$u=(rU^vPg&4DmyM)#gREPAiVYx9XOn)?@bib3muQ*9 z=3t;lYUSTszSm*1!Y4oR$gWsf6w;`qS-Ysbai+l&TxMfRkMEZW*^+$OYuuUcYJjo~ z_h&QRYuc;X6T|PMt|kf8^=mK$L<3J|d+jo*_gvE{5f-i*Wk`5z$h{es9SK8ci6o1# z*g~TA=lI*TY_wZP7v!B>OSLO5$sW3j6!WH}icLC2k`=%^(K@~Uy%o)}w~II%=#s>3 z{<`2GGIZI}|JpD(cacK-b@E&hywTdY&UZ9atkcO1aV!celcLkxVih|B_&tnI$F#H* z=e8-LWBWM3pS&l=+u|p$xMPh zr~|6P-bdu@1rz%4CnF9vm0>qVe#)a!1cu9jlexnp$uHXjGail;LSD8t*mtk&JkeN7 zjjgRKV;0P1b6brI$*TMSS|zp~32Nk{*w)8!)s}v}f&Zjy6UcvZU5TCIJP7NGV0Hc@ z7%^FhSFUO>P8^^7(p4s+&Kyk6@grSp?AFNV=&Quh;HN^Sx&8&5wttrZ+HQ71D~n02 zz>a16ysVa$C?X%-%zG6?i9GTwr@?L+0IT2S{J>05%Du5Cn=S7F`wp9IgOvhFuDuu> z8zyG;kI5W>_FfIqApaJhH8Z!*nc-ilKp;BlSJc%qG!BL6OKdPbgKc$i4g|aH3NM4mV1tARNWkDR zVicc;pFXnKOPn}>xmp$W*2&&YLcvZod4>>4fQ19Ka&tmNzt(Ta5p(?UEN z>3rE-jd4Ly`zfsYJPF0MDNXk6yMbJPc%@++1qUg&*Olu0B*L}o-1nmhZ%V`~C781c zQvs550)z5tA6W>&aN8PDP8_%*YYcL7dk1(1y;#wA!C4Yygj636zf8YDl08BV-D%BT! z#GWIy#LOM(6CkbDnodkc(&+Qoaw)AKN5^%?YdF>ybpK{%_O)?r zhcC0roNC@&eip=+xr+SNd9%yYgZgE|N$H^ut4;yf|Dd)cZWS7yr}+!*wMDar>JZP! zW2Ydk;&OI)Om25~?pQiA)pDvyl%=Wa7$p@`CH+m9D%BLTvH_X)Ro}BhUb@Aqt;1lh z_#C9Gf245`fQz~{GKI}B!yeMt<5yU3|Lq?6hc8_J`NA%WVYm23M!j4weAP6$E+_rp zRemb~hgWe*LRcD)k?ZoUEoh5b4=ia8)AYz5XA9ig4uGt4*8CHK^Km*ghwe5IGY?S77>)O>*lq|p z)0AsLcb8{tj{WRxa2UV&?69ZT)#`iRZ-piwNY_`J31ydE@=BkHC1qVNDrX#R-W7Lz z`7?@HdpCN`ei-gXf79ogKMCn8$$0jQXg=!B&}mSy#cWxxPi%sV!-c=3>AU;~EWHlO z?|(nG|BDp`u(EReU)>D-DSNt3)SXkhM{uC=oxJcosjx-B6=g>^F}!Y?W$Nhk50i>S z(`y>heXA|*V5r{?1E)5VMlNkr!*i87&UBBl)uFtpP@BT{SyLL_} z0KT33&%2$|L%onY;HUB9ShE#gw*jyS^yj}eashK+G&b%3xO=_nMbUr*jeq@-hJl8r zxy@5~b>YwO`-ZtynZF*+b!Ft_hVXvQbI{^`btO8J8MVCjyT-#$0p7NaeIvxxD2D%h zzMcY!g#4jAXtjqVt4vj|j(yArfrpT}TQP zDiJK2PKs%(sgyDv-#JQ({Yh`JmIJmGFKozPE>;JZ*n1xB#t2R{)C*i}lj&EJZQ*Vt z`%0yV1A)i|FJ$IpWtO*b_JFJCh+>g*hkt3L{{4idl7bX-#sAEsu6P#&tAXN4l|8i8 zb7zv;&C7~e`O*?^cj5mgp@g$$LaG>D=C19ysV^vmNq<{nCq&X{E}W>HN<ypwJvl)v#crT`_8aJ2FK2TVZRtnFn1yK;=eErsw+qmG5jV?m()57) zgtGPsmszfoGso{tTrOuD#q)n#X?2xducs;R#&SH{$E+}FPZ*DOa%8Uo=}&n$g-St>(S4LZa47tt7u;X% zol;+7Ibav9)(-z$wzFIy#7Somov8nUa>ZS@C1iZ4(x+ng(E@bn)QIPo15Q$YEy(Eq z-Ef-vP$EtBUj;v9e&?Q&rA3oTk!!%cLU{T1j#JGE%ubGRk3*iDtV-tM@rO_lLx!pv zyu2EubB>n79EY=t%gHP=c2W9r8UE1qnIhvv2`d&heNYaL4DFV4t>@=N%}s6otv6|l zCK@aFyo0`ZAh9?j$yLq)@*i~Ki*sJz)?35$4yl7SUdKGxXvVt#M6HI-#XC*>ph4aM3<(E^F4;+K;$>F`%jsR~hJ`?`blocP4sdP-2) zV?e!1HwmOU*W5cqxL5FEupsL0^qZAv`+}BAA_?EUn!3@1<_J3O_6yGvkFAxfN|rOj zCIR5KTxj`Ts@75|OpQ+4D*}i4u;ve#6UEs{~@rcQvZQ_&A9G7y?yd+)9H9Hk3C(HTu(SVks zhnl|}GeDISnX!Ema3;5S1jPWZ zXs4d?SJTNZ6??c2&jKJ*&82&94xI>f?x0kQds8Hix?HP!9Sp-{0{9z=tp=CsXW=^( ztG&YEl=zvnCr-6b20rJvo3@sC1eFb#xW(XqXW~Wmer2J!{vdJn<}Z3OCdU@jE~Uax z&Q`yzIvs{&lC5J)j*4@|ONAtK&GYRTtO+O4<2w9N5)(M#VL#&G!Wx*`TA$8r(?nP$ zI`LfV`ifXKXM88Q8^c?q5~!eM79QR{(=28W$o2_H7DQ&ToId@vqYYjQR6Rh4QYNQL z#uu&>6GgqN)0lpF3`vtGY_;(*cM6urD+19>fnVI8nm}}4jwVw@8a}}0sg;{h8H55F z)U1GTfYjgES zUertM9iuJf1mSZ9gwDmIZPRKz0)9kCJBYsB*|l=< zPkwH)2stw zmCY+RTk(h@FA~#x6GF+&u{!Pqu3ouiv}3tt@ylb9qpt90 z!2={I&^lp@6HB>WD^$ zQe8vISjaPEU`5@axI(#xX|le$UjB}yuGYnTH0?I^<+R7-O-05`@+~p<`1g#|Gdw=1 zOECgzjB&a8A3FxVYijPNMz>99bUD>`>4uz}v@fyBn?V3eWs^a2VR4Ci^GYAs&_U6T z27xKn0QhOVT6CZVY=w~;gRZ-|{y#5^U_%<1T1~XY=cWf4`3Slv5q$k_JTB(aou;Ki zwx$n@c9Ttd59@OoV8@Og^Zf?Sn-=4Q&zG$sYyGck=hSP%e3t+ACX`KW^forfXGS5% zGA)U!h1L7y7?qoS_Ljiik(o z&z$jR5-35VhIlGxctGWft$EdXhj&PV!!V!5ow@59#UUY$@SR+D>*0thtN55_zWHoa z=a)6BmzIw&^bOCY4(Pkz>Jw)?W)pC`h6^jDwk;h`NZu52-fbyK zeON#*CBtU1!L-zr+p#hGo7x+9^nD^3;^hp|S_wSFckR$AT)2CT7KgTDf9}632$~#~3{T%W zHTGE=b4rI)uIsAmSrTR(ixu|BN4MA&lN>;9WW_CD(xXm7%MpKqry-;|NQ{Jw58bKy z47?DE66J!gC|>vmvEn97khgA=!(4|&+QG*u@z}I?jiaNCi^zokro-S$rCS?`j`d7C zM^>?Y#v-z*_p9o1ETy{P0u_cXgWbE!sSRvaf}>MiUpCoE=HB4I$6dHxNTO6^D;03j zO_TEc7LY<0Ye4iWgU!je;HW#_F|DI9#=31nth@VdC8S@i1io&6uq@m4Ha zYKmezn4P2htHU2iJZ4$9nK4$1T?YVYC&Grux67QEp3}h) zx9vsW){w-U`gVl-q2`lAwwM9V6_4*|U2XOer{|HoN!x~<)ZevQO@9z$-1$x6ISc-N z__8m$_9@D>O&`vF{kL$smtf@guw$Z1=7@>ExGTAi*PI<&`QL&4^=&XM+}V+TS9Bel{@Pwgszwjz!WtXa8wu5ZoGw3pYD?H% zI9a_@Bx-EJ?kwRM-mpEu%45Te8$ZtfqjsV)Hxj(ePQrvrbS+^_Vm74OXc9G6DeUi} zNyC$ZIm32!f!Weoe$uala%lzx6#xYEPhYI3=!BFm&zbtinV;aUKZ+k@LMY8F8*3Q5KMd0y({y%}oW4g4q^=HWeo$r+eZqK_?O@IEMdl6s58-UTrTG zj{TfY=&H1&9|?dKYI5aad~01-tRB%S=9?b|sjEe%sL;((6{5DeFxP+^hIpe?sR8c3 zP9v7f#XHsa&G`C#>wim`T+2o&0llP48O9}c-2e%y4%HDR$Ta-jvz*j{! z&76({*LC)-G0ErlAz#@e_*nBZ<;Bwf|5cN_5+Q&Rcsf*s8Mz? z^hd1T&5!$pXCIRs44I#6U-RM(VY!S5d@B#n-#~K3J#OirWS2-to>bt%-W{8Z0$u;l zH6Twx#rprrF48Rvz$gLS9L)dUoRdKZ85VZN7e!t~2eWM%q&S{4H6J z;M2jWUem*Eao+IiT1Nurg2nhk1N}%U_WNzWYtY(EJ>2Is{a$X7t0-af|*d_!c z$0lX6Y=;L|1rgEJ3t6Ue}aY(%O4+-X@35S!2s>BmJH}xa&$8it~z#KY48(pFWi{kNw*6KKA+k_Iuk4IoF7m@7ghAt(f&l>{E(UOn~6_ zc;;tGPe0y_B_3oA>qw(EQbT$`OQ7Jw@q$m3zx_{ZuV21yoXs7u_oZj+{bd-GXcb|t zm}3i1=6jPKg>gh_pCQ!tO!U8Sd&}|dAU{9v$J4uIAYSo@v~Rr?0~-6S{!t6ACP7R~ zu)Q8JC-tveS1osPA?Tk3#6LUzoAkn-q@!q zzw@%>^$ErzZDYk7*}HUoOg_tP5$j+VVV1$3Lsw3Bha-dGjQkVuFDx){TgxN&pgFWd z7|(c%klyE_`2}l6)P9-%h_kGGZkHO!-U2l$V;>Z0Ce*X=EZA;nbUm`-;9!ovOBG-XBzC1L-k*rf{J z3roZe%UKX~$f+7TG#rT<_KXyK-}w(8*dj(p9wIPh9nV@yMNH=cuTpqG3K1}iE^!;A z#DyTj7TVt;rThvnfcm@WF1<4xUh|)8Q4TSrnT&dL_lh|{=ztQ_3MPgf=y3Do<;X(dcRGy|qD z9H_ZkgS03h{fr3=QRyp;6KecE+nw6=YsUH6QWFE5>S9^ge|~WYY%GBH^O2YT>P4Ig$>_x$k-{YFW1XKW1Srv?b{Xx?7tb#GyoNV9@-!Y~vX1;meULdz$DK3r;~+^fT(( zyVBVPZZmC&3w48Xkq1u*K(5xJMY^|DSHYmlW>PVx^Dy#|Hw4-l zo1l9jlwHCYo{PMB#7--u@F5DywjTpn%>}yr2>lU|%h}2UuZ|nd9KxPpjV`%*2jI5P) zxxV4`eS7v`C;u3DYL8&k^}PB6PsB&(@l7Lkn6T6=UGsfhHN@sN`EDsuyeL4pNK|;p zDrzJ|>x9{PIf19uhTf$Fmq@*Huz?_rGDd6Co=4_eh>-l#aQ!LcqW>xBGVY9yQ9}2i z3a!|kvG>kqR%4%f!57JClta}Wvp9r#1W8jb_g+Ks&IEFq?%6kFz{_Ef?!-k%Kwsy~ zC@2dR@c{vIC1NH=b6S3%8XW)=UrW%i(!y<@Q)`eM#peJF-|A$I%jrc0Ycpr~4=7Ie zXsd=Wn{ht79#{{9q#W2GiCBlH8JOW6(+XAF*T8t)Na7^b`WPW8P;{Y8)_o~Z6@4sg zcC00phakLp<}6g;oMtn7K>61w=5HyRL)#t{1ix-vMHtIq z6(zy$FG=tlc^Rv-c`h97hUCBA&QSK6l#c<|KXWrox4UyQHjcH_>VlPK-xO7AeBnf( zA8xp9o%vRYLnEbt`~#qqE#4x3AwTTW!yQ@Y^x;glQMM)6>_|!FW?sgM4{c&H;i}8H zrT(U0Hna_fEefBf2xtUc@A1$d1ogxu4CT89qW628Vo4ii9i7WKdfIhXb1@B#~ zPbWq_&W(71v>6ALA2tdRC-89$UTm@{xp&*qzzP2)%K9;6Nt z>h+GCQUfm7HB+bNIIc-SlGP)PTFn;O47k{T7gm!REnmH#NnYTb%d{Zc8lii5rw;8B z!$ooMN;WbL+a5%OrQ}P^L~u?!OHRw+O)1Lz{kgihqM$Aeb$?Yg(`qonLcd96UKz)^ z3Ij|_?WG_Ds{pKD(e~6N)kwHWZ6yb*yPgc8M9wS2vj)*s?!NZ|2L8J4)2?c z)g^pn@0?L-$8DxH8hPwlm%)#-YVSV3u*aCyaL}*CK|tiVA%`otYEwOc>$4d(x{OmU ze0%kVYu&Z7HN|ah3s1%1Gt2{GJqu!5kZF!X$mO!hflnrubnD9T@Eju}`0Vq9OKA?`T>s}NxTN4x$)So7znm(Eybio+T0TjQNkorO!*7+sXXW<#Y@4-x=yURlH zA6!^c7Xau`@@!XRs%jKa`R9by0qowAZx`@})9NQ1AOV!hXiyrELqnhZu9kUyqN}y0 zt?xpQqpt#2cn4Q`?mJha6H`UBYQ<|y2WoKm%J_pou)r@YY~r1c5jok3QH+fp$Tj^k z;(tWrEC)n9sw@sbr4kl5s%+dzZIysjDQ}pt04#0JI|M*7@$qT~_u=pCwB=iFd;%qI zPkD7hK=1Jx8?4ek_y2w2pvKwzk7L3pxJYg^`yUcmIr%CC(`uXx8Vq6NDC%z#ji=CH zD>ySG=T6-x6c~-wNUQCVf$At&@*)0|D6yi1O>_DU3&j;vA^B!va!0d5{##GMGqB^U z0jy4+(X)~Sy`(_N{ioYI1Jl8*=ManCG%JfD0S#Al?|bR3W9aEOavcAasgt+4V;tn| zm5E;sUh#lnSAni9<4iz`Ve}Vfqm(Ms0|2)T!AYZWKs)z8J5VKXd!2)X=g zmhX8Bb?|(g>+bi5ZB_6jpGjO{i5>DoOx-pKxbYvg!ks(!Njy0m&g8JJT3ycM=B9vF z!MGj1ws93n@2x?}esB3#pFcKW=S{!sHXmi|VjT^`y|A_j68|ck2gQNkjIEheGXhRF zVkjH#Rh^}SV4aFmbr?TC^>3eO%J*E4l}@+eR4Bdo{G$}A|A25hNi=LB^&QBPVgn0p zMDhR7k+OefgE;J%btrWR7gJC@jWDPjMgC4a%As?|;07d!ZI;J|vrb|d7V^=|z8Vfq zix9%`dZ|z~4jeEfKz#14g|tO_1OPOnK7wM?Qp`|%j5Tz&0PA#7WlLfo zg=gqpuV0X8PGJ?}V4Nw)1&m)bjL6p|v7hVJw!&c=uHP@g)b55UkUQke#FP_>IPAwOo3HH^A>X9@QB zF4miNfruKL5EnMsw>e$&{GJFRf3gxw;=R0V*mkal$DMZppK{pv+A^Q4Yktx2yt{W3 zT>))G9vT?o91oR{$c=4Fh04nZIH0h_2B`T`~(1*iUwy5tTd2^Z+Real`J^b zCwmX>GdOPvqgPuhH+$j`7xNKR;$< zBW_}exLK8fwb$S=G#MXjOHWlyZC}Z4&t}-qdUE#RQR|RR>#?_IZB%3`x}zRzbOca? z)P^q9)c|^?(EI4%5@x!Yp+wRQ<|_Uw>!H~;IZPom{^Qv$Xkx*BN5yc`SU!EGh5s|H zV<29xF@a_RQxyYp-au*eVy!;mh{<@^^BB7*K{lj{77J{EmAv2J)q_L}) z1x2lOy2AjulD@s^U=>&kOWNEaaY;-y1)x%I3c|rBOS}9|t@@=V{S1!cQrQ!3VlfR4 zo1i_cKR&sVNZ~}@wYXRJI4;l}e{(8Z<21W}owx6z|9KSX*CbydsjAx+;SUeoN`!>Rr)>8K&&cw2FN1`~6-(B?m8X+ekrOOGv>?`R zOvU!Iw1O6N=;@XfdXKZ1yQrKpWz_7c*SdLDH4*DLVIf@7R6=fVuB)t0=5A(Bi`Yp9 z0@NDrr4q+-lqzm+g>GJ0YEz(ENEzU{g~xnSPBS*@uz7m)@OUD#tnhk@;d#a=ExF<5 z_y7}iRwW4x90E0$#NcwEhdqN2-4Yv$knE! zF?_hMt`b?7Qpf93^$Zgvm><`TJf$^EP ztu^g6sl9>#mc1%q6x_Dy+5CWCo7y-pz5kjiE-gIaUGx{eJgE_NPJzwZ6gYTZYDuM< zv|WXbUZ5AuZN5LMY1Ieey8>{Le2TN0=9f?iCaJB<&=ug_8R6gzM%bm~%@lF$zEB)t zopGEneSMjV;FuP&QkWkqibls4F}Af>q&QH-6x&%#Ye!C=$WnO|>udBJot#HLBwjEJ zt>4Z6RZwaRv7Bqpkq2@>QqO+1;#vI}iEOkD<_I%K=U#1b3DpuN26}TzlbEB+4?+qj22?-G z1@weuk7kGToI*`3rjFu{L~1nEzaYUa8ilVRb5r#JNwEjE*K=dCc^tP=5YA+oL81P$ zXo!*xwOr%B~uPTingny>ksQ_$x+QJdm@`%A{cx;V~ZXsY6tU2GJhgl(0%hGRa zWTxd~w;Q#Kb3B4x8R|C<1!S1Y=Ma7bDlXBf&Y~#ATUP@1egnkrQLd6sp$&V9$Q^l=V!U#sl#mVMjDNq!sd z7hG6(LFQavi6}s)YFznoS*3S8Fd*$gqn21waCVj^Eh*Hnj5Jq*f9l{1uk1%|UG68{ z>%j8+61<-WRqmTQK~PPkpX1+5;MC*r<3GjQI~ddc@O(u+=0Uow=J63}R)Y)1Kahxv zcee~OM6X0lz?aSwW=qRGzlH49AAhO+rK<-B@72xMRR{nIUX3vgt_~5IMUX|q%V6Pu z!s?H;@^T?VAY#J5rT$2IsGS>>vix%=w~DU;DBlxHA!uv{ym^i-3VEB1Uj1tw=+&?( zH6(;pjvygJT_IDt_iWEyRb7F3WBHA8&OmE40yuH2)HlJ)|HIZfH3!;2TROIF+h)f} z2OV^5+cw^yW81cEcWm2sIyUB;s<}9)YJS4L+k36`to^gULM}jxo8tyHZ&bsF0lrZ2 zMM~%j(CqkI2FNZb7h3796d!hjvd88w3*U5$sm5J!;>c;(%{3*Lba+ip;7E|hB_$bs zh{dTpnBI+R-NdiZ`a`WFh=0jZqQiF*pi-mxe=`Oh%o8wNer4T#hqrrTMOj7pZlHr>z?nr+)%xh_8-Zjo=GbrA~jB7f;fVM$8kxJJV z{3x}%86CpVI*h&s?*YrD;vq34#(zGnau}|IGjus@jdoVMUc{a;vVbLeMmT z!%fg860ej2@>@5SAxUWD349(&<@0LE27-P6Bi^IMVF18Kz60}G1!?`ubanZmClUjW=;CCN zSeUx#AV{riAh%ZLP-;E=dU=R(ol^_cY|PM)Xun2Lobkf;l;21XT!7ojD|LJ}u0X0- zpHAydI8JI6=xatR^YX1Y8NCOOV~()o+%1|6KQ)yit^cA<=1!VZ8mU9<&9Jmen&9*j zN~SE3a%)fieUGB(B&SoiKCL(0*lOERs7D2Zna~?wn@r(HfP~_0(6JnirP%pa@OqSbdU6AZlYi&bPGz=vJ`~@9(S9Rn9<+ecQ`-%vz&H4%7VL={`%i zMUc9J&2^q+9(0g8Tmo{KD*PHOv}v8~3O}}5iUO5Ybp%OffB$%zMn1ioQV~!mo;6|S zfuF7wgs1W|OK1a4r~@%v#|#7(8>|Gkr)9GDh#0xgar9iW7($hkyejE#I}(GLODW)w z^tGR^Z*X1&XumnIe)&!zFwOWm4q%-Aig~?0E}`Jn zwLI$IFt(&!A(S~dtzMpR`rXuwmlV6ye(hSjDNiiTl3`JMNT5L(XgmSjI&W?Lifs0A z8}}8{Wfavw8`N^ z*8Zqv1xL8G^KU-Kj3Q`Ppe~vC5HxX>hSKHd?4=TU{MbC!swk~|sR&GA`rHpy4h!5E zjWY}-E%y2a!&%l{{U74wODpuQrcN5lC3YKKV~rCV;-kL6#g?g$G-~ou=K4A*@e86~ zzJ~Omo|UE|`93-L9~+Cu&dvaD*P7Jkg*9lln<$K+q98lhQxFtxm*faq9{<@F1$7ig-mZbnT zB(7;r2>O&d`pgx1?(T#0Fm7hLRuqJcr5K!{o{ob1ni?_GEM#bGyAGM_W1j*AZiMyp zf{2aJoMWN2rAdG&5&|l^gl?!-m#+Y)^<4g$aRjraVx$HeCFdE;4SxAj2T909*ME0d zY5f?rdws}iym-k&MrR^F7!E`B_exWQ?zCSj4z#0K_40G4L>w49(#ixfRlA5IlQB6Q z;>Jb9yBw#@m7Vaej2-RK%~6Oyr*{;($Zs)r%jfh?`fomGOAEEuyHkNji4?mONIx-mIPmT<~%TSc;DXD;x^-k>U0=J%vuW;plO8Bp6^Ej5C z%H%jstCG|NBdYU&84Aka;b+;gVDbwa?gGLe{M9(98IGzk`%T-@aqvO9XEg_VhYPxR zz{T4p+A4q*x32Qa7q9besc_DZQ&HRXPKYqYU7$(=J+I$UP`a$chJ`A6J5n2!?B$!= zhxdx`CK&9*U5z}t56=Yn%g#)C1LiflJ5H{-kM*poKYZO(U%AFqVpR zkX^+2?8q&1{;5l3wBonoXTmSps7&pB=e?pSR|!;F!eU^nsMa_SO$4-JmnNVyIfitl z@Yw6j74i#cORIr3Dk(%!UC&zxsVl`K%!wN**z8kxZRzxIbf{*d$_ zL0y1*^{rrDC26*Yuy_-`%`QnUw@@;$87bjOT&JF;`Dzut#6`ZQMRk^Ms&K9+xt52&;inUAjD3d!<`%dvU_H5z+ox zfODDYKH)x$_^g?`C{TYh>oNo>O`Y})y_J?}oRP!oJ_`unW2t}nj}&-Nl@I>^sRjR! z8{lQ(`Je9l)qmW8NqhX(JN+HB%hWSC%AbA^c0`YrpH~RXMJj4j1p;Z;nlI0UB0u9q z^vu*`WSUi4=CYGJx_qF5`aKBPkb8+9MEZd@lqu4Xu3{gaFGMuIz?s&s?lqfT{S$$Y zi)^8f*HVdn`yHh3*Q1*e;JYQX>&W2m-o#4KcELO}I;k_CkLTA!w^Bb%aqixGk;)?S zPmzq~8(zT|@Bhl<6%FqOvcv05JVrI1;?V>Bq&RgC5*bYgjBD}On$AhqM0|;~+)6`x z{azRLdV4mFT)3cdatTLH{stYfLcZ_}`Nqs5=k5jB$9z232t9031EpxU^47hkr?8W= zwV?^shxER3UpLR-R4d<0C8z0MZ|U2cOMGXQix2mzjCu@ZNC4l*QSHqmf+-Lvo=xkkrB zW~~f{*2{U#KkTqT;K+lA=cF|WEG#nT1D-0BvD6|_%8kj6)(l&MUZDE1NM+qgDYJJ} z{94N%qI26jP~kUx>d**VmlhoHMX8Xp8qK3n=)j8nx=q#qP+LZRJh^s`X=-RoT}$am zD>0wS{@6d@QIJ9rXc@!$hd5+#Q=g0#8HU)hNoP(xKYW(B4sY?6fvg`yQ#J{OA)1?EH+!}oV*{VfM(poQd2 zthT#U3eBm2g0$2T@VZ(&*oKxtDLSMmD~k?=mf3Lz-f-vTRX#m%@ePmzf^k3Rhpm>w z=q9nov67lAmq)WkmtpKmwAD1-jVoOIKkN;&##eNqH z*{PhJWLjttlenRuLKcx$Xx^Pl_Mvt(>j#89xY&qsC-c>~y-rsk zTuB%(p){sopC-61r`q`l{t=}G&gpMVC*-jYU4X%F00&CkPDh~V8sh+mmX(er$TGqP zgvRU0Z{2+L!-gWGnd?_yHrlF^c5)-dCWJw8>>_YMD6?k0-BofzmF$LZXv>HERBo+B z$HQ?90ROcbDDlGyR8jUmLcB(p8|Pl{Ol&+sU!EgT^YLynBlyJD{Ge}|roV5 zl}j z=;KI$ck17Y^=8bCsd-u`gz{jy$9p-^Q?;i-J@(#>d~d3|V=ubQ_j~+fk^EE~(2QF3 zIv5@q21yC#V4t#5QWQpcr`jhq+M>g#Ui%SN(?I}c&1l|AT34rw?ZQw1?pk#V)Z`|o zaY-{$%FyONqDy0y6HIkRFJ~$o*aeZRJ{wsI&YOvnjSVvV0cBkAaciCs{krujZEq}K z#Mp3!&)ZMzD^pcHcOXhZf8q%P;t9`;?8G)zRdNRNnD~!039Pc*QBu7fSMiyYPNEXt zm}JiM=xZyrpmmmpt=bp8_wAX-#@_hTl$mhKdkbU7BuoBK_usf;vv`OpPx~Lq-uDPYvJim^y`5>1|2{U`8BGi!KDfTkb&77P7XuZkR4lrEqE>?nUBXqZeR zjO(9u)1emsddn4R<#QqVto`DY=;%?%QaWc09HW_?GsX%mXNSKFTpw>q0*7obX*yCn z^6%N6c^JAPco(`DPxIT#XvF7`o1hR2i{oeR$7C7m5cDyeyf=g7}* z8>2FV6&mg2ZHZ8?JYO}ulHb>(`O`_8Zt%Sn+C*cHzPSwFc?c0An`kk3xx#6WUK=0` z+uP3Fk$gj2c#`NC$2W|z2+5vjFRm%E zz0M**U^!I9Ge4I6{Tj{jyjvJYl7_TaZwoR+(*pRxpxZ$Ce?LQRN3Gg1@Ld~g!+2VF z7``YvZbE8~rqanbI5D8-V!Z@gikUd7xq-`LR@^PF%*y0&XIJ8_VwPs}nN%}f$L=s! zk!y7k=74adiep=TT4LDNj+6fvfxRR zEMn{H;&q^DX5kREj3Em8>`2pSP$BK^w<*JEc9Uo~r^b}qG80YPGPUC^HmYxYevtj{7d}W?hMD~hDO(al_aZX-p;#A~$+QFq(36}q#6t?bVEqw+PJ84Y zS_7&OPYESI=+?Z&EI`v#+C-r(M4E6^=Lxk?1jvZvD%+29$oBUc6$gjBf1dxUB=BC> z`gY8R%=QPAK_4 zR0X5OLd}#9K;qDZcW)drA=m`x-7~mXPATME!|z_SiXl!%CUOfSswMmNgxCHCM|2X` zjpn53W&y>B8oVDBB(Ge6ezDcr7QPP0g=uJ3&gv?T7>qdu}pybBU1XV*K*=62^zAG{g+?aN0qA2E^Yy=xqs@n zkDmm)I>CBHsPcuVvfZ8n6Z0iP-0w?GQ!A{MJk0#m5h6_k`}jgwCZ4$YRnrJ-Yp(3e z38|V*Kz1>~C}zuaQ_^2vMm&jU;MJj_%NwTVnwZloO?%W_4_we*IGd=(lZddirny(l zSBLcrBt-ALp5)|6%ffJL9jpDFeU)aYmA<_|&f(E;or#(1W(bZGt>yG`cfNLs1ZU*2 z6x4~FvX4#!$*aKMM*2acS`NISg@^oT7)2HT`$kV#mlZqd$4653&L0C~|@@pGAvJVfzu^dz7am??c>Y^Z5h{C$10yj}e|FUWlx4C1lf<`&^Px!q z9xgX*uN!+%4-0zB{P)&BqP_373u#cdA3ltO^mkB4PE3bga(zGZB;LP{wC?0_AXD-B zB{R^|pgUk*0R0s$joO0O54EI49mGpzT{t}_^WIU!Y@?4#jKfce^GIbNJp8lMn_f6` z6qM2k4!?|w_`1e{&zQtk@*ws>eO7eZG17+~Y`V$yY-pAdB6n)G`k|p%;>s-u3GEav zyv2*UPM3T^V~Cd{6(G-mZ?O3bJb3dl*}BXIPJ2k~hwl!IxNeC1`|(*IU+TERA2Ywt zdC$25HLg4q{Z}prP*Ztri+8Y`ClTOYqEmCLT9FhOK6K9;(XEnRYcvHoci_@dtQss+Jg zG9%K}>z=nV!=rck!}at-qkeupN47)eEj`$Z)8_^vclI%k(&i(+u{jVdb2QI$?<1mu z(4Va^-N9TqyLtMGHG`vsL%$BA!KpfLQcF=3fb~W@`B2+xNryilx?cpqV}uD_cYQ-d17B zoyswGQ~gZ5>X)0I8+E;l_Sh>(LaesN_@8SCr{a@8R7c%Ui!%*eG-jm4?18FVLd&4p zTdx%3)kvDIrGD-|<7M|9M4)3E)LV^}LSLq3c4ND25OJVgCLz$qe;CW**W>c zg3F^K<5FCJ;9F<#rXpJJLzWBarincab*q+@R*;oH6wZbOTW%JOh%@Ijm8pEKdheb$ zRj2?i6CEbX2B^K3@G(SvOnyGRjMp26aiMk%o9WAmwPz`ItB*#*EkkNNkh%^LK4kE= znn;5GY7M7x_gM2^ctxm~wrEE9i=Zx|>yk;#42)fvr8t5?6l&p4TiQ{xO8m7P zZ0v+XNn?cNaCrW34z-+?DLs;Nu1EA^Ey6>)b6l`;8y)McXL?9u#-D#i_|=46)77}{ zLyT9sS@L*VO-NEdXnadSjuBPgWX#dP^N)d>;UhV_u#qNW&|KsY;?xyFW#icAn3~!@ z>b;Aw2;dWUvC`Lf%bkF`t3uNVQyh*tj|XhIX*xSk%-yZ$v!$OhDq~p4^%2zv$TGgj z6&ljXR7yd&G<8$P5T0?@q)i^LIric%5eQDKGScWYu|OQXl(X-j{JIniS?(08T6vU~ z{sw0HAG+A9fVXCeT;ZdKl-;h;W6ei^-woxO19|jaY-NReml4%+;@t*%*1Ty}Te~Es z?h_Vy&55nq|D=+h7n5P~T+t0yGk9f9wwj-<94FvIDw(HV)AQY?w^C0-`Z9@@lJ+5ENdV>L}QOIlXzWmK847@)72L~&8Y@~j|?dV@vV&5m%9Ej-o)|?H` zca~}~{a8_EJ=$8y;4X6)HW{i5=6UW4r1&Uy#~32K-39y1$oaABmT^6>8&2dYa8*!i>Z{tO>3}HU*!A`R$GwhFy>5Z+3-~}6 z)I0un^vQVg*Z!sYxWpJ|u3s5psO$0o%<4a10yp{i9DNMC;=|k-_^b?LFBZrTe5JbX zpYUUQ+*=B{)y6rM8{i#r|MtR;e|U{dEd94FiraLMPH)?ERo+^dv2UN7p;xTTngB>cSh)Fj zH=V2N3%o!l%z+iBPR@ey{g=u4Ux>?)j&v-2D{9Y)`YEz(<~tY+A4C}9;qmTPAi=P} zr_Xr**LSExr4i;>3P*-HL{NG>4Oi=Wk5<_PF}5e_nWSDHk)uRpBddB3hOM-B@Cy&s<&N2R%sly-Bp?ytbpCmA>Mp~UMVuTGEe$2;90Wzo^t zhi{a>6Pak`au*MsJD1JA6bst%nfCVM@AG?D_l^H%cMlmC-O_i??y9{1iIpPp=vZ-C z{>U`r8o3hM`SN@nX(g`bC)8cfJ$`4uE-_SK`osgZezGLwD%trML#i!(lr61onv5I; z3ao(%{PnkU6Dt(Orvc!XiE0a@-S?GE$I!u9G53cjI=-q7Rj9&A)l=7ySD)zwn|&*B z-;_$nktOh^IS;GjhdY1idig_%O0mT6i}7&X-rf_$nq75+#3otIUU47i=oNf9HH>j# zK}Jg_8j^{Q@UAXcjDvx%5os^p1&r?-@d!{TGZ$+5e0^W|{A#1gN4r`)lFJb9_HeXg z7tD^>PLkD)nRuS4ol%HnATza73ntcacnqjVO&g@bN}0yOQu}Ks<49)S!;$0UKo(O@ ztvY&j!K=wXF@J(3$ew-Y75-=z+Js>=Bw1hrb%fVu1jPp1;5Z_S3fDSP?B$_q@!rh6`xUJ zg`5gW_^G_$K^Cnlt8saL3z!R5Ww3k0FPk_$8ugL?N$!BtiNGXQZojoPoivhZ&5MPppd`2&jTkEiMC=mecnC=BUgs8D1a#y-q@~m;;w;jwn$vh|BlpxW5jfo1}8!q9f z0Uclk|GKLp8tf2mzM6)i=&xa+A9|Q^=#7~YsnMH5ZB;m_GZ4c$wB}~KSZJGF^JgoX z-Nv*kSt>j{xK6EGpGpL@5y^crEHk~^s0NywpILQpgI+SSfeYT`8TPXN>7{_2ad>3M? zjHlKEpp^QZo`NSgw}?>X1bf2fDC|}dZa3()a|(;lU2LmFJZxGc8(~yV5JB!5ZRd}c zW$PX_LQ(MIz%uq^aAhW5;4lAPuWurFD-|Z=aY9D%VX(W_VHVf7(Q^Bh#Zb)mFR4A{ zX5`7uuyb0)X6#I)5^u4koQs-<^Kd502-~g2JPFh{GZTZG{?V=#Qg~!Msx|nU1*`iU z_L?1jK1R1W9wIILv>mDmChtj5Be~$5f@$3~gb=W3Kpur~ZqEYmXZOkycK>MM`m$4_ zt}XU_votGIH-}axVn?W4ST+|A*gk~v-ZLYb!XN=5)9G+_+sCtHO(BL`q)g)te2{qv z+vWXJyMUA}%Me#Ymee^NI(B*RdssrJJAdZ3y1mSZ8R10bao7OAzl#H!Ls>9G<(IDf zfTgF3?Q$`Mse+5i&P;Gf_Ia$;Q7y0Z@}j!24;W@rjht04(D zGI1QiU=0Qrlbo0q9{84ls|ov|NP+u>ETa`#a&2;7Np46CSB5F7H)K>}Ay0Ni(^X2Z z@!GADY^BkVuV?S;BN+Sy>jljBP|>?~piiO!qzMe_Oajx=YQm`1-u7zcPw-PMOMY7l zlwV`nmn%3}IL_of8PP?Owa%O6Z^P&+)jix7&I+-uUmbwfGLJu6GY9s3`P7?3#l`w% zGR+S?pAmRMj0%P8R1&jYHP@3TSZ>%&&wU8q(0pY3O`Yg1*@ZzXlzBjWV8 zsbM+`DKl?wx^L6ZQX?nZKUjz3HWyrgB~FW=4oj2LlpDL%(y3cI2Btq;eNGA4x{n0^+v$XS|$`jZN=T{ z%n(Jv67PGkRZ1uIvkvliNqD#y=7b>CRd*?&G^8}LnV8IV;0TCgf22<>DB5cQvTZtxaMN#zf4BOc(_ET$1)(JDveQ|dEsBDB;U(Ei$i#YmE>8Kn^sQNs$jbl{I;&=nM4v@84((2zMISBon%$iW?al2bh*-MoFk&Z!v5NhxZh2 zLy2*)CAoV|wG7q=!THKCtfAL})+Hip(zq7cV`zYsf@ZqDQ*PpkfsKUVe}>f-s7d>gd@{895l!al&fw zi4y9=vSP90fD<}K!LqctapY}18u`+5~Kakfn~#h=g3Lv{Mj6CQ9|~>&R0tx+?T@uCx!I`b%4rM z=NBD@(*B>4G4bAD{=UncGprOISfa$_MM2RbH$va%%=P zEwz*Be}avPEQ2x6Mu?MEqo)O0K<)~vts0;8uei%L*zm9x0FQ}EW0vZi*43CN;VbQN z*}ts3ONz@aSbenndrqf*1X_M3cW~Lszu_k5;snCgm@>HOh;DP{_=DgMq)qWNdRrE9 z@aXQKj(9-X&z8Frr*|W=nG{N4HMo-G5vkuAN!V_%VG%LVt9K}S?ap^{Ye=$87+Xpx zbZNQHzila!1Lsnk`HoMuP3q($NhxT>w;A)ddAW&vunvcHd?v~N9`4@i+yYa5mORr; zma*nzm!!O31KH@r&ihLA2o7-amB+47f6zlR$QWDMQnZg=4Dc#X?JxQAEW@>4Y+A>z z6H~0e;;=m3pdhcg6sTiog)VQ2I1ND8o?eZe<9%4n0{er$QH~2O>TE6-${X#EnuCE@ zP0~-KQr#g3;-o8VcJ*kmq>_LAKlY@|8DqeD9h*#|NY(yhA>3y*d)Zup^av54SqY zACf6p0U_rdbTY}qH`H*_0LqG7*(12m4%&i^pcOp>Uu?KBBc1kf-mT6Y4aZ#_5wJN$ z9G-1a_Slr`upt}R60#pM=RZP`8qQCaIgyI44to@9R?H+bG!`w@=78wN)K9 zxc5s+5k&_>Ai&4vcy~qa-X&qz_>!QpCTGj20hfVhdBM-rbpmj@6OSYq|DOw(l143-uQ zqZ09CgUdZ_Wa~dc1*$gLA;H@VN###?R(46mO!4F(n?QeVm3dGyjGE*VJ5Q)k%culE?ug!+|D0W0q6mnxJ3!)EVZt^;H?= z-{mA8GpuNzNnT-!sdO-s^$f=}>`jCw|P#nh-cR&eG@%T0>0WBMbNk6H&R1&Y_+K4qqC4D+ru23LfJ!> z>eDk_zsT-L&>>%AYdKlL zpTve1=@Y(urDfM((%@Anl%eUOb3A#GIm4}L)yYD1uFSauoI0+%G!$9?Cg-;=0%+u$ zsbX{_1hjdV6ZG!Rxq(N(mb)&60y&^8!{^%zd&N8!G;#a+VTxV;{=GNBlqKgs8*^D# zfiSLqkoR9$d(HQs`e0{cv&@3;r#DN}grlVZQ}bqqsv!*?2p16!yFOPnJr zF@#Bf{lWfPJKZ zRAIc+4OG4J=#Bzrq=ee@gH%Q{0WYlE2>aA=VUE7Qe;uQmn?|=nttJ{y6(x&DFZf74 zeKQLmTs>f4_(p?(aoB{u&?SPOuh!Rt2VQKl|0M6SA8!BV{&xE91Y|l|ry{B5v-Pt;erDb5x z|6Sq-Zx&Lo-<#*_eQE3N^va))S5APGuC4novb7Qao092U z%OdT&b@=Jx>%zF#dBFRrwq;?Yf5@3yuydA4h`IM8LLa&93EVHo?Fr(hB&66wnIY_q z4cfVMVq!-6ZT0(N@-yv+q_TjId&!K)x1%_K(g5q~$eVWagog5;#W`1s3Z^8Jo(4@U z(BF5rAe2M&?a}2-6I%*2=wy{+qR{(Yq|#;_k`x^^B9JKY2x|{T)2~L6uTkJv<6EDM zI%nhYx`F&MBcyA+kb!c z>`9i&;fTGR94M`>v7c;169$q^-^L{i2M zRxH+KD1XQ(MpjfZJP4*Njzem2deCtaM?@H~;$hw}8+3=__IH9gvLq2kQCxJu>-ai* zUs{a#cR>>Aro|0;)YHGK7sxrgqPH)eOqLjkMCVP$y`S|1j^r+_a5|SZ(8a5c9UsCX zZ9m42-v+2F?8-2lM}jK5VFoHFp3rEr^ceb>e0pKCKDxg>3gv(wLxsI0Nc>G|i}NwZ z?&DG=V2vj#!k9<-svBo5$wy2;Spi!!zm9xkg6m#R<>pXEXUw>JEm^4&=C1|T@4=_N zkujB;ievxE87}D2IIf@PGF(flCfT@Rf%y4N>6d*}YwES)2Z~V|S`+)fH=tzbf&>mp zUA&S_pVMzW+HgM*w*&8>beO@Y^gIe4xGiXxZeqy<;%2tJ{DR(M>FSp6~vB-LQ z)G=E*SS##5V*@cD_I;|xV#1l?O;7XYrw@bGK?l4otnP!2&hOWSo|4&y6^(PPl{3xt zUe;_L#WT~YCXXC@^p5Vs9?qmS=(V$B)#cB{TQ*uI2mi%RFB4a+^6nE8&#aiC}e#3|jaw7A)54 z*6zuQA}gwsmhLK(j*24$4sb?;r~?`!GUqj3899i%GZynyi%Z;LXHfZ63TLu}@LsJ` zp~RiZhuj%Z4__L<=AsLp6ouKwuGR3g2(fq4h1-T19L^M`Yj@toHf}ES>xbpfOGhuE zsnDt@MT9@7qC=ea*#kSQ+=XYAf59VTYh73>*~tbRE0#mqS5x}2`^-=}SR4E}@JC}9 z#poERGt&yv2rdtpi%NL;C4zsV_KUV9$E^wn(rz`%ZV%T2>;bTP@^xkg>EgFd#DwIO zN8^DN!u*!63?V`lAE9C8n)$H$BC(p5oCVRw;w#hX4U=ZnszFLl{}syj+gjU?5E-G` zxGl`BK$W!Sa@a30>PN=(loDDSOcO1$wP6$s$TXG|*$xnXPFJ`xg+nH1w;xh+tSbj1 z>4^|3tUbX!=R=%Lb-)RCq{Ed!S056)(AR?lVb#AEsZA9v1D@nmjf2Q}1T+Wv>Mf#}Yyv}ai|ec6)GLc|LT@)NyA>4pKngspNsu&gx& zF@-dM=SME(x8yqK$tV5IVujUbMaEm>+tljH#mTQ$_yED5zje`W46Mo$$T!4heZzKV z9Jy?hH+6GJ{n0gHy_JXen(+TIHILUATGzgU!mNcGiQ=q&JyvhrsV8TRXxud7+ z$?ip!&eI`iV?AcImr3F$P=*_mLe-zqU0; zUjhwzaGDXl9CgD=PXOnKRm6A@#-}+B9$rDc=u@z75PsFAcxkVC9;#69hN8d=yCl`p_ zxD5Y1&8&Did(CF>!k*ZIRpov2RF6mRL}QCMXa`v{oCo|b%NSC zVp&|Vo!@_G9q@Aj>X&lcNd(-NEKgLe$z7`SsW{))ICqf)=3?+?C|eyyOb%L5n{3ecmxGNNHk`?9>dz4|KD)s3K&5CA&y9 zUlX6}QN0FRCkMapg;-Jb(sxEP!XsQ|wXtOeP)e`crQlOaXIHUD0^VnQQl|$f=!-}e z7*?EL|H5bqCOcKxy=w05f&z}*4M+@$_lWj#k#{h^<1h|T+jfw@yO628{tB3j!u^W{ ztpA(pzXJSjZ@p9^9Qcjyn1C_B54vi=g^*@OCw>~a8;9gLadZtbSFqb_vgAhyHathL z!Q()yo9kBLUeBJMg!g7r-&S=o_h_jjyRo2(?@*Ac=4-)9Yt;*!xt3;3np0oWSnSAi z`@Qq#1hxB}*dNn#${P3dvbcNUs79W)qwv6)oh%iUl9M(5$j zz&wQqojfGi=9u(-P9#aYv#mALiR%3k0t-Jx+hK=1Cevhpt%CYw(wBJO2ie)zALi+z zA76MRPaOyGy$hd@;@QNQAoP`O0`%K6A^A)+owBj#{V>vj(od$K3<`qkOlj|!Ff!t^ z9KVtxCA1|f-{jd`301$6m9zFTpgRc6K28ZU>Mtr?N^m#a)*lBP`JB<9YPl(# z66mP_JMK!{!QK15j-Kzs!y|BT*LswS)UXWug%@i8Cd`m`BEnm*$6-@qpupp#BQ`l9 zPmMEoRns&NjZ8d%iY&YG7u!+dx^_p0x>2hwKKrp!Nz~z=gSQE&5w5$E7Qn;v6PkD6 zN*^CH663~BlrrmVnzL(T!B8@bx+^Tr;~2r2j108lD42e;7zNSNGZ;AA>e`Jr4aRnx zuBK~?Av7^L49k2bxIA|{URZTJFPit}?F?uJoq5mCLOd$# zT|G4r;z9g}vDD9>VwVbwrBpsU4;>P!un_~Td!#c_g>tKG;v0wqmxy6QjB+0s3zmXZ z*_eZBu_Cs6I~oQ<5(mKRgDP15NJVzBhhaYt5nH<;YmB2dDk@HaJj&Z6#8H)p%G9&l zH$tz79LLfhZX+)jF*h|>CG6TDQOEyLPJUsw+!eruPc??{N)ZK_5ZLt*~9>?TI!ORa1Ak_rTz;-bN`p-I`ABIL$ z<6@Dp=fK0^QR8~-vh9G}B)fnXR#EY&ew_Rc0)SykR(=8RL4* z-0IQ2si$zU=f4ttb2By`96k@Hs9nXduo%r~bj3=BjWaeAq=Oqb{KDNIvwi9lxK+2yO$lO)@X-T^i4`c#6SXdFCw$ojn@ZKbz@htGbym zeu%Tios%h=6{?2y{gPz!H+U3;<+B&<;#VPM0m=I9DjI1rIo3EwPs5t|m#}mC4t8ku z{0e8e2nzT(-S5nL`V0HQcwfU3>)Y1=ZJAxdx_;MYZ`Hn;JZrB_AHjYyYQ+*1ML^3S zg7n)Hvymv{!2C2A34^}&OwC=*nnw_a!MH!SiU`|EWG5bUI&bLjADS<_dqtB3MY-ml z@Eiz26vWLGR@~9zR1lR&JkDZhq3aFn@xzre&VArOjku~#fl9w=$VjdOzLi_(ajDs4 zE@oIiAA?5Lp@c3%x{M)fRW03#VY5%Jrm>uCL;FefqO4U+-^b)h`JsO5_0q(UBKf(H zBcDG{)m8D&iH-ezBqP~Ykn`hx%^eon%jo;@Ew38wXL+PGf}bB-nQGLwN`@aIiHg;) z3Pyn0)MWyu{FfGoX7sO8YTHO|sXbfW(uH7k6MMF4l6=~@kt9uQBt^_boyE*T$7+fk zhR87~jz6BIW5p_HG>?W!7EOA;`af~J{i1OO+M4)6*GYr$Yc4TN{vf+U;G%d!VVa?+ z8iiau`ZY5#g1Y!;-bp?!vVmka>{~UIC=~%AXD@9!!HFYJEyX-7a)UVTsqE~*^fmp- zdoC4h!BtP))N*c<_QYlrH8($3)Z_>tFo<37#6~7B_qjdAl*F&N#i>)$3UJ$|PKj(X zxTefeBbg7>5ws8Ur8Mo-2b8E4x8+NXA{A{$tt-TAQtWAyjJ_1El(wW!0(w@M{>%ZS z?hPkHh!*OM-cW`^vfHAj1w?eB3~S}$h8yjU^2EnJx^yUAaJ^=^Zy&J;(Xe?b2g!r+ zx0Ux$=-s&~Y}J$W;d6grsR&BGcUAA)zaHR))=*{r=;N+8ZpVVHUOitQxjpy?*4=Ns zHvKLAn;WzVJZRUYTQcNlNw)O-qUQnTk67eq;~ZSOo~0<+EpoJhsX z7EXn%$bZ}hkqf^H)jybx#&<&D+sEhS;H)fwAoiQA!Czu}*c+wqj#G0{VprVn@ZT=( zy20;1%3O2>_!qKl!-s{Cl}cbJ2tuYV$zUN8R^9GoXXkNjR<0*%Ye zBQcucYY_oKM@ZoZF(LeeLkTL=B4SV8B>R)Gz%`Hhd)NzXJS}g*pZzZ)B+)4htr$odk>vE)zxx-&zw#WPUtV1PgpSq_ z>OiGrp;@a6ERk?cgo>gtYqaf3w>mgQwUqNHmKD;2IB7vqKCegD(mqRfZ^Zr!G~hv* zC$vo%WHQ)gVIbR<5d90-E_-$2zyTjsxjg60GFn|TD6jd`4c)E^Ijn}1Zv9k^B1UgW z;0#4M89bLDADOdF8B?nYD_@MBh1a95aU7MaqmEd7+gX{Plt*#HIp>MXPC$9KKuv0# zLg||>EziAZbDnXP5$)ezc-WgPRrU!^f5;LD3K>iL;F< z?pUaxfRbHlEl+r<~I()oE@?$Xzv92KJLy;y1Gad$~AMjhf3Yl}$! zFlW_P;fLK9a4B(YT2Ac=&qqHj)DO4&y3GznEDhQFT(P0+x|8I}^;9PiTYpI|uJgh? z*OP=EhX-sa<5^KuCU2BT)5!;eDK)~mP4XaPeskTYv)Kc{a1Em)g-*{AR(CkQ%N5vm zr9@Ag+`NBK!am6s>>u|3!`3-xk9qqK?GuXellk zBgzB(-mP+(6$r9UZH5I53llkpMlv*co%f}6Jkl)0bs&8I~Ui+GDWM!Q|P+zbPVf4aleBE=28x9nXHwjw?z_B z(k+elUYgcF;#ZLV4bR{FqUnLk&Z!C*UwHW^9HML7We1J3jjc*>$B^M9^BQH^VO;*M zo2iO(;If;NXEH7jIGxA@(K@_=kEDbCln%DMdO($dSuqDsO=sluZ8J4w1Ik-HF@xHd zG29dN@!oZfoY_}cwIgG;CF$XPlFzZ+-PxnOo-?+sGL)zN77+dQZ8f!p&HB-J>g@?o zux>P4^gKXT!_Fd|+RL!d#Gk_4ZeI>x{Et=Y8Hlr|mCBoPPD~FMqdyyAeLc zF8!10nWvJ$owzWID{yulUr+K5(T{j zr?Mjk#3aK)zyCuZa5KD?^paS9Wwgr%DtoVnDLdX?{3+rj#$~k?L$p%zyLA(=;Nuj< z)9%(cf3yP4PhI>B3nqwkLzteEkflN%=Z1H#Wh{>jM4GwM^m~3V!LAA2Wi+Bm;I$Z{ zO_yBxpMd2fAO4=SR}KsoS?H!y+=jpU(+61<-aBq5s__r(Cs}Vc%brD9miZ^mpZC{D z75_D(n3G6^*AMAWUb3Dr0a^$0*G?I{!P(>VUhMpzef}R}$EW`zE3q-9?$3dc0vOqt z{zqK;50tF+?muw}X_`H=&j9J)0Oae5fqf|Af`yy22ISpI{J&ogO;L>X?LT7Fp4ni} zRVC{_XqURf2mb)yX7jhXntybarmUtXkJT++f%$UZ^^Y4u0KQPS_P2&_n~48`lFU>_hwHqep0VBtQqr+uI(?+N+dZ~zU{JBlfh_{Yf;;$U;J&lxtm%bzR)IEr&+@OG< z5I*%+EI&|C3z2ioSvF(p>EoxjR78F{z}GE?Lnj`v6T2Y6tXv#5%Huh~-t=uhJLY#! zK-l+!l~`p}Bc(-P?Kr)=05Glv>sssnvQG_Q`Y@UuFuDc?wKW>ac!?ZH%T~qQX8K7k zE;O?AY>L?-G{oIWbwexY?%C$+J_zP#Dsr#(&wuDAmND|t2wmDfC;`|a-3!+QQan@^ zg_t>_<_kopW4$}QyIPo3wTiuK4Hdn6XxteGt{Yo^@nqU^f zQ1-+IkY3f&Kc+B<^Q}^j4X^m1l-mt<4@3^fWQXiq++ zH(847g#p13B{`9?2M$VF`FRokJ)_PTk-BNPUbEM5PgHY=lE4CGhYuZ`F_a78bDZ7; zin^dcJU~DOE)N&nS3FREOAtPz9%b=7QLG*buwe1a=+miy_=88W-fDI7=;pzrW+8h< zIGNM)!3RGVRtB<0C+kn{{VaIPH-^pkCnT-quZ|%;fp#l#nbr|ESf5xxWC;{Uh}--= z+D_SWt!*T+FOZv*>)`O8^on|aw1z>Vt&QEors-YUL$eVSDVZs-9LQ1-US`ac(S1ls(EW z>miV)=+g`*!RipCp1O|hO&DY)fTnF22zM3?5TwEM>m%1dtk9-UUEBx758H3NTwYwV zx89AABCYv5p6V}_xiRt%3KM$OEUMzJkE@~9E~1($rXc9sbhCfAn`V&=Q2Awm0ggK_ z0*q)gQ$)g=I8DUHTGkhnkfJS(Au7QLetS3hA%M3$_K~;L+?F?Gb!j{|LsHMwBD>Av zP$rVQTK=6h0g?4z8+K7{MqZ0UFFI%jpn>(!<8Jl~z)YC!+KT>;^P*)z={nrXNu1)1 z45gB4%oAu}<-TCrfU?}B@#QT%1{4KKPyJ3+ciLcp9Z@g|@30x6k;GozhJrd$7)c3d zYQ4lYsVs=yZk~7A8A-11^vS*}&c3)UT6|40q>m;pSARwxNP;S(Zcq2$9<+(I+~RmO z9Z3z2xm&MeeD0ICNfL-y?YUdg4#zEci$(H=OnRb&n>j^7>!m5cEwC_q1d#bBuwR6B zi@(Tq*=5sybKJcTUV8XufiH^r4>(-Rgqe7!vZQCePs>LO|Ks1C_=A;S>6&N1w{%i1 zv#_eU2tmdYm^e)wmo&wgPV?E#29eM{W>oWN76ng<#OcCR!=NRmb?V-g#T!#+hPADy z_8~{m8w#J)5nQ~u^mfiJ11ecf%QLFIeTvnYMvSn#XIU0|I{C!QoAKpmgmlbTJuBj| z+*_`7-j|Y1-1W#!rC5lD6X8S?=mqo=nRmOA2XbYV(OA(ST=8;GX%_ewcv2;m4xqvM zY-wtS%b5Wgz*tTirU=2QF%D3)mo8;G8+>D&RWPLPvPQK$u8i&K05{0?WEN9F9C3xh z0R-HT=|)edW6g5U?Jtyl@$Kbm43vuqHihgr$Ze3{4?NDHs^=!9 znhN?`?FQth@Ys1RQd@0%a!Qu*F@F+X_HwH=kBJ)a5V5>g{FgT0w*$4!bX{lbc+=h^ z+FJ}=6h_wGB~x|^0opH)>WUojcYg9XK;KcD^|_(};nuF-!$dtGc<@#f?!VFC6&P@9 zqxtO&JNX>mnmsS`I0KoOqa+Chjv#bc267#eQuCutIAQ`%3dl5T2g8e!NbaKO9Jk1lRzp8rYhMHZ3GJO zZqXkc&=ipzY!F`1h&NmDE2rI|{i?YkKqJa)>~t`=43u?M`conI4tAMXO2@FUb@&R= zv5ajR1KOOX4miHDPOgMi96k^kv#fBV-K6_YlC4p3B7ED_OnaIi02<~jB@KjnI7#GDGU^aafh)?Q zk!zJSpMrm6v|3AHH4-X0ofIlaX6qA5eVJwk6!zNK3!ohm$sU|h**##JvChOvR{!Pk zIKlA@UpOY8cNuNhG>CXY5t*a<2YpV7>^J3Xr{6tv}^otiFow^#KQsR5*)%Ik>8C7fOYq;t6( zDAGBt3_$rPm=Yw_o@<`R9mt9On3hEw_S^jz=8RvJS-6%v7!D6iX|$VmE4oI?WA6?0 z{lPEdD0DjbZ<{Y2W<&PxMWE0-_uR4+9_B!Ty*U7lc?|h&bhXykc?u#QUIlYNk=-moQ>wIG2R`d=C*g!Y@n$! z8D>$OXE}oRo7^Qnio2U~ccnj`h{HltQUbSJmuV&Rq8C){-+z&Z{nQ-iHWS-eQIFEo zU%qS_bt$ii4+$#S(UxD~-SsUB-O#TdAKoucruyO=7UP=ufZ1CLqsBtR0!rjiks=m7 z>Hut~s8(?p$(S7umsg%1gOmX$J#bfExEO9Ol@LuA9QJ>8e-B!N?w6W>999M0Yadj`M?^Tk8Carz?V}@kB--Qbx3enQ&vbmsP#tpF5=jCrsZo3IED;M0 z-Ahex&%y8`S%DyzIqzCO_^4|E#y4GmiUZu475AF)&;EH_4><(e*5eFQ^kP*$hdV9( zl+<#tgTJYs3fKjO0PMUgEtsHaoYnaWyVj_q3#HY6HDCCMRmb~3WlHu%MmMitkeQ=V z#Q5iEKG?lA1$tp?nyQI|O+*Z(l*4}Lyc{q$qRgyARgBM$cV$?ateWyz?$%<`(gBJy zD}*!ik@nFw4IO{~=xGtZ3h^(X?7PENEJY`u!dMp36tis zhZ>REzeyHBSOpcayJ~(gt_${8$snw36y;D}qrwmC@(lL5G`K^H1*6>~Glin;j8WuV z8u2(H2guF`PPryI(z>M9#cMEJTmpzYa@bVmkGL2%QVuD#(UavM9ixJSD;TM*(ltQh z$#RVi7HiRv5JUY(?WuRDUu`M3)Yht(_G=B_UIM3|08o|Z={;W}_J1$39!sYlm5sMb z)qI@t^!#-EzQMnF2G$7o9xTn}tn2?e)+=Hov31}ny3d(&5%hqclfTEYy#PkHm?ijL z%Wt~Rr+0ohC~hU+x&(+U7PN1273&~$t19BsszR%Ur%n~J>36>#7DxZl^7`Ag_`WKQ zhW%0P`y*bP`5@wRz!)#gy;v%WvK>}z1iK7PQw54P+g2PjF>d;$Y8f|o-m$ewuwKFL z?5&^tLs(67-$4o@1Sn9QNC5ONnruu8zTm;_!U6IOuyMnq4YgeVj|TG}vk3TSd#LiQ zg3FzX&_JTdqu@$cm@?7@D^`S>6>{2fN{joNJQD~%5h>;H>#n~JL~{J3cE9)rnLLZ3 zna~|Ox|GahTOckI`HyzV2r0UamRj;ZTwcECD1NWhq)<54{RKdC5@%|~Uwp_4!-vec zpEab|cZ|=7*HwbE)$tQfC=rM!es*cYAofUQ47i!8xj4_|wr|n)WM{zoP-koG(g(v9 zPC66smu+R@>w^FafMBpgmCt!q@-_j2c_NzWA1@EO$uUtx*BaJ9xf{G!=$v&3-5uTv zmW%b%anN7DZU~^OtpJQ=*dn&ZR#ClXenX2AvPGddW(y~un@#kWK_N(hl)YcXmcL8! zox7DV&u!qMb+rNcu~MBfWP7rYmZw^6Fj3R+OF1sT6PaeHJ%Q<1(EfIZQ7`y|uf+ZmA-rK$A7VD8tK*lmq0?CAy z8DIIiG&x?w`BJb|f>FklwenwH6Ui9$I*zH;4+ov>1`APCtj(;%sF7jRw=SrL9 zi2%^W0s=b6NPl$bV7grrx<4Ns)x_+XjUxeQi%b{@~d zUV3y_3WmJSHUDiN^82o7+IlaBHr`L^90083mv?Nv_(Z5qwY5IwgmO)EqK6B*t-+xV zL@Iy0#sVZwkUHEhOyzt=<`FSX-s*Shb0I7**jLXl=r*XFi#OR;nlDt?rK_Jxd%#Ou zTZJytj2~vZ1irQu;$^CfS?&a}H0XGF2?qWG;cgzpvl^UwyH<}-x60OB1$hcmI|4Em zcsV+ikM#RjyyFwJcJBgtgZpOOi+vrst9R@RdbG)7mrHk*b7%hI4{~`L7W`;{^f21K zba7%&D>8oQeWl7%ZLsJqI}C#RH(W^n&01)al_i*+zj{%PtzUphB7ge;tETbD8jr5% zbx3bi4rs)+BSugr+@Li#ZEtSGM+O`vs=LG-@wl-sxO~sBBT&s(md;hMZNu{$C&j@K z<+=qt&-U7XKGmSxsNwA3iYBjD?Y`!XaXZr~7+H5Je zAjrpjq{CNC*swVyr+qFz`kMhmyt~pLAip&C{QuoVfLp;Zv;8;lj10ie_CK14f2)|r z|GkRY#h+*X7nT7j5GVq8mn3h(|!dDtjHG*YQDwH^}MM)vZ~krE?H zpO(hz!*ww;@aGX@8bf$Tc}K2Sp#SCeZQC*qISX=^W_m*Vq!@;Oz0(;d*L zz2gjEF#LG*T=Z7)Vn(Jay*s~oVT&Npo=t%$UO9qH{d5O>_Ma|-{M%1Z{%dnx z?us)w%?%gi{(H4rh58AmdffhJy)Nxry%SI|`?NXwej?zz!e3k(=N{wHn7`q-=JyHx z%_F#Zv)`Bz!ss?@*Qf^iqv3A>(Or5uO)vSb@l%=j!FGD}Vl%Ct`#8MSi;vOP*4Y(O z_FT5;?d+c(-xc@;xyc%g0G?i?b9=X{K)z!VeC7T2~q!Tz1z9_9{66rO{}0G{t>pP=I4`nq6Z6 z)F@Qf4e6w3=2O?S(T*SE&FfXyj{<;EY_3&?=IOQbx=9gsTQ-={tbt=DK>Y z%{GaKnq<#8;hD7QE2_76hkrl63gvfkun-VVzh*Xopo*@%4Qa3-Id;5mNdJWBP-u>s z%E;ezD{Wbpfd?k*fZ;d4-HKf9wtvD{lh=ITao`Ggw($o3(fD^`Ey%l1E?S&Snr1_6 znn0`bNL5#ct#)H#vuzqSYz+W$g1NCcyrH-LxrMbQUEIpZT%>0%Z4a!2zTY2o4)?R~ z$Jtz8&lm*HuzPh7mLaNIRiDRPYE)ey3FVboD!NrP-Qu@hefBOB1&AeMd#2x|!xE)N=k2CtG<7!Lp?3-4IpGjd|w z@t5OtQWSEt;7#d8m(u&A3_Qffs$>Efb)CBsarum$O!99t^pD1B(c?N^Oa)nCay z4vkr{kiW5WVxE^K3@X)dt(&2nktaHMBQ0-bbW;)y7VsRO z7>2vbed%J{D?gY1kWK)|a^~;y5>xTQMhB`#Vc=YQay+dvO$;9e=$cwPL19Je^McjK zb20BrdDEDI-9$k`rshh*;|Cr!Qem(Q%A><`pvl@}5_a0Vk~R&wMn-u9Ysp8S=XSC# zk02@8f$?M5+>sm#S3r99vOxEMJ=A%Rm>@kJR9%wyI|LHb6k7oVo#2nHYuCVEEHs7%S8>YG^aBFekeHl0ZjSexX(;7$2!#=t;lz!uGa z;5=)IL<3|olyQMcEe^J{gO`2-ap<;K*~#>Pl0xECGjV(M8`Be6L&o3D^t>+t<=L56 zlu~D$k=KyFfcgPER$OZ_gX{b7s}JDNTA&;PIBY_=;quUu;Z)2_g$K6x4Azhl(#W5M zN%cLg#^N>H{zjMuC85JSSfS4Y$92#fS@!W7EiQ{dn+2K3cp7z&D?7U~W0M1~yQ5{A zuK0&+MfB6bjATKnWnY_OxY3W9={9SFrfA{7wP@|ClBxi5<+h2S zbN*;jz#q`fw?Q~o3Sc?yHYXHzEAXm!i-I>5{Wdn8F=ykDPQY^&vhw>rOkEhuBZZMI%*Z=~}ZvA3YYK2BVhnv;|RRXiB7ccBEi z^kkWaqd6Gh5s$fxi;Lk&Tb*EKh-GMdw7kNl0U2Jhg+H-vad<>2{fZ* zUjJ{+3-zlj=K-+@c&sqlw|^xZ0k+EbHJsl0=!ZDKwkB^y!zOYDno=RC-Jv*~IOk~Z z@r%Hy&61AMM29^M=gNeNcC$ED%rYk)?=T~Y*2NRzQHVgNLLA?Jb!9s7^427W4$?P( zuN78ahCH4`Lm%hj(PLV$=8~v6s3MT@anIki1&8+8xw#n*7mq`u=&NIX;KO43t62Y=ZhbfxFKO84F@7;aWt-#B}k%F{ObW`S92Syz8t0W2+NT z*i>Kk{D`aIb!EtE{jkPs3xqTQ4^{OZsiuO@3p4kh?s)Pq^F`2vFM%185zN z7yD;^_|ty@fO4hjf%s@!%a+eM_o%6V2gtiA;5$)ov4_U3=~Q{}V=0*ehuX)u7Ig)c zgz+A3+d2D$#p9(IZW|d_s0W`n?REp=m?ckW4CaZt)PrgFAK!y-$IMAYm8yrfD|z?` z`Vc>*EHDH`ntfq2;CVlL)CiYe+yf%OUs%&so0c4E(m9h;c2b$sOA_zF%~rG|E$EPU z>FxV@ond!Bbn=|AIi}5KIpwu)^+LjLV3h0rGNmNzL`?UBRZSSk>+XU*$yos2xr_MQ`>4#=*IR^>1N*- z?DY1HcYxjch|lv}BE2}W;7mqNnd+64Xh29!if&8aw(%fsPrqvFOT`OKV@HHEEO>$0 zgjSrF#uY+&^VI0fhmQfb4;n_hrg3OL8AVv0%7Hn*mKXQ#Wr&5W}9jSD< z;GIgG*9qYWaa9eakcqnOd1s)X0df0|zBS<5zzkf~QMxrV1fzRk&ih?^&y%&=mv{5B zUJv0$#f?ch1V8{78=y-h71f3JD0y)`3V=m365x1}+g;nu=V_8NT$~0H9&^kWD;gd> zGt(Y~CYGs$$u6i7$y|c(3S9^{*$Cn&I`e0vV083{-O5#LUQtaet5-Vrsuof7wMv;L zEBE3TjRfgK4I|I7MQ&3mWK1gfRm)yFv+F1ZKGNafnULzK6hcKmO&D8dn zLy;D4v~Fl#*fnx6;9PJS={ai1cUtt4mzf-6k{H9vnkTgBx)87_a#Cx_a8f+sMDyuG zI~xQ=uFLrp7c+~_oh&a7$w;PdzmoJZO+iB?KD8Cx&i22Y+nlHv-+zUYEVS7?>B{cU zag%$TpWOnu`gq1uOFRO)p zp%_Mc>(wW+P}jvssGhOv^L#~E8DwIv*CFVoSJMDs16O=`0z@QhLcr#o;c6aL0QncN z{=r!H>yeyWv^^;hWU=Yy{<8LsyL%b@jV3(P*&QM(cd3m`mn_-{7ba_oBm8Q&x}7fy zFKL5j;t4m_8g*Vltq zHN%V{8aD@GzE+-|MxxE@lp~rI=TWYv97^@v0I1VSsVTX6f4d+59>gx4-0x4jz*MW= z*s*bo7zsBIeN3o$-AQMyRSu4m=M~WzAQAxPDo`jm?_{2m=m85kU)oWQ@C{25YzskT zqyCO#q!IdsU+7BAf!W?FS?cexa^}Pi?)oV+r$LY}U~|T|Oui^+{e7B}te|tw1k<6> zo(62t+=2KsCb>Od6gzP8G9}8~zLX+M(n7&UJuy0on8ZRV;oqj8okG7=5<{}Y+L!?% zLb9S{#H}yqjECTQ@n+Y>J%`#=o`c7Z&1Y8Ry5q(m$Hlg%B(cfxKv)jX~t8T z^}t{#MqI{d5bG|>*BoD5>w2&wgn0x|BBZdxqYPCqNh7=*F587T0Ocj~Ip}vuCC;}* zezr)vW2&26dqA=|L*;a}Q=ZYwGZxx<>i4g3C%GoXoi6Vyj_l3EQoPD}V)pMWmOOMwzKcPNb2YYw z<(UWO$Qp#^m6PRNoSKH74IeI?lUq?Lmu%5Yd4ySZMpeFC^dF{X8Zz=8v#{HLtg zbgh1j^;c|(QcT6FL#URS+>-X&;Q5bH1zl;SC)l7%`N7rvJ+eLa=|NSSChXH!wd6zE z@gMt{?FB96S+IKY!BLr=2SmiCDbxxIJX9(ROoK^-7l|Y1sf;?sX>}42tZ~cc&wpy| zrGRJ2b4>+No5r|pc&au)N{8?AErT-mrFuU@HR^Y4NCqHCQ1>Zp07+xd0DTkM+8v`7 z|Frx$D`}*&xlxM2&Y>fWds#tvRm5p!LawSBY5d~OtzO}6!8!N#{&3h3`G;eI+7H9* zTQ?c=r;IcQ1DsreJMiKb9%{x%7K_8>94>+XuJiM#1UL?jL>g7V7h@f7hez%-P#MGL z8kUbp?SK>J8$Lf*#t8dgiSsD~Svxbz|Ah(PPYd$DYn*>A4m)E4GAV$8h2{S*ai)1> z<4D@8UtXv$5ZhWFX@SK^JOmm9>>e0eiNXk4&7C9yQ>T9c_=_l{lBABGWm-!y8@pRn zsUiwqX_Y2>F`8{|5%*gCKhc2z_eEEMI$F5x#G#w7+jwB@gX) zQ+~d>J^}aSjYX`U*QvAmn9ZTGv|IwGZ9aZ)lb)Mbn6J#6C;PK$rHKf@TDhoxQLkIP z*=Rmo@V+&-rLWLk3Oi$8pNVp7S|bgci~9UOo^OYzFaDdq5#uv~oqkMO{6;{0Js><# zKlc-10fhU8U2ILLUUF2!%Mc=8y(wGV?z4Gft$Eurqx6ZY zcq{NeaJJVCXpJtedAwSx{`b%3D!)z+dfwq?FEp){tR$%5PvIxn7!Oju{&x?}N+@e6 z7=Rg{H8r(U42bBQRv=1W8IJ5EpxV_snvWT2&#wIDV78b3iS|()!&(qrWMo z9F2>RtHXf7-weXZqDeWY=^{m!qV2cgWq2rjo3m=Yf1Oum9}ohG5-^VKjaCnZxR9cfcuH=pUaUcr0hlb=+cm3wga+9ZZ1f5!5m{K!n;@TfFi*+2 zr8N`$3BKQ1WImozqThpBi5K&axQCL3?2Gr5^8Mu-@Brsb>0QESJEa$Q1}p7i_iI{2 zi|Y3}dl`B1#70*E!K?0Oi<-}7G0g|yE%DFTAn!l;cn#4^FHbg2&7BtOa0UDy1wcvR zQPcp=hK4@roccs(Rc(`=ufycHAU_f+OOG@>D$rYA%sm#q55m}di6N3$wX8x{y!661FR1>yNd+ce##R#kD2MaUJM* zzg0&()O7eE5QnRp^L{A@a}Cw35#Xp|<3UWLiR&sC9<@)X1so=1x(rW{*HXX?%yQwn zYvI;w?k`PwsCL?WJmIr^1mSP`u~O`})KHd^)fH(wylzljbNegYZl${-n8amD(hr46 zUzq`t$sM*Qqd>1E_ee&KKDsLV1Lf?hecfuV*Fcxqt3B8!j-s4tk3`z;WdQD?#pqyo zTdzcn{yyrrP4K?sG(!mR;W9@S>mXD9NS^w&D!zigc_J04{@}D`cCn_fCE{2Rg|6WP zi*RU36WJJ+QXy^;-@FGDV|t15_nnt3V-LQGEl$C$Cxj$wS{oh&VZYIBJ)rKIJ+^-@ z^pvt~rgkPwy^ptl$lCv=8!(m~_!S~B$nO(NbjqhM%`fZw2^$dCi!5ToRAeuIZw65j zQMrl3*mmm@h%5mlg9he-yTc+lGQcE{WR7|U^&D=Usg$IeQwZ~i25AX(Jb+xaXl0z# zrXE8Q_EFnTu9*`(mIh`Tb6lPTRpFD`I&>*m{$#IuYW7NC(H;<=VdrD}WP zF@Uhffp8V=2uqK`f;Exes9~@_O+82hRW#TfSBd-xm*#j%#pm`La)TAlBBe1AWD?1w zVhpiqiL70Fk!|{m7StzwY;sCzOSn5e3(T{89eO#HkO?n~1&^l+KIv2v_z&x=BRIb4 zPs1}@7od%~yI%Fl5RlKx!YM?-MJy{Di_l1B;eyja&DeR&*X;>C|$iK3NJHe%aOaVz4GOX8WYyjzw$X6k%HU*xMw0XyP zW&!J;Q!D%JiGi`9%(cWKN(x>Zk|kig4+?yeO)%YT!fEQF7@&+%!wgyx-^+yU#!aVt z<-TKRvWhkeANoCkt_)oavwv73$czf!2hw8eExOJ8N8Zsga(UT-Eg!@IzuOHT9Z95y zhHo$eFFTb=@mC$UqS;Phl@&|}lL>NDS((OID5r^|YQG%OVYQpM_H{Hp2a%8s6P-1` z61iq9Ho{?Y0^ntz)9;VPsu>C`^SlWW!S)swqy{5-=lJ3xp&Bk9$jiL%SGY#dq;y z#fiR*(}XR!fQ~8@+-bp8E2RMw1h685zUwT7#p0MUmLp1;)6GDi zB%tccf;*o2DMo@0E2K&~s>`JY*du4S`?zju3-1@EuzaDnpDVEk@a85xOLp8A!u4vN z%U}U%Y8w9&EFiZ7&r5a2g-*O*szt(TYc!ll{c>m`c-qfejAp4!Nt7a&INp;wCV8Me z;L7!4`^wcqVepcMz1gt{*US33y){^-1W-QyOAG`55e>g^fj(^FNZY2{RAbH0*t^c9 za|Jbiff%lH-7SR!I7g|-8T03|%4Z*5Vf6G7!3e&QdX7V|Ix&zV<~|?OKK&m6sRp$w z%t(+t6O`@{t*3+8UN=|~LWb47393ZgrP5(!?{9+XUdpwBALOw0Txaw?(h4#C6yO*E zi-f>qb6X)`T#kLhRTWulzixKsZ^NmfhX01@Fe4p3=%UyMb9?t0LZ*WD1-8nQs2eg! zi7BmoUOM}j+rfUCeD`@=>!<4D1i9;yKEzF#Dcnx-%ITDq4Y|f@DbqSW5|fI0Nd@vB zA=A_yU%Q)z?-wz!GN~-yNiH0E7=UKFw%sd`QtFjlmF$W$Hpdzh*I#E&>WX)uFV3DM zdbx00glY%0T^l_w$hXuvw3+JzVf4wu!%eb*Itciu$U;T0WYht6XnqD>8My=?dXyhk*z2CD!F{!Y&8e0`jU%YgM?9vCSRTlG`0xkq=#)8^J;1|5_$m%K zcYZ{=o1Zv$&EXtieYw36h`Ok zRq@)b#sCKgfv{->tUO9D5fP!}qz9@00B71YJmHJG%#gy}ywuyjeSg@p0$I2LbqsYz zTel_lvc=a(^~aH zv9)dYk8t>c8-_}Uhn1tOl!#ISI2dzL%6`Qauwt+8JW+CGYTYqn-?aDaAQ%2vOvn^; zE*oACiM*QT6q`u=#Ap2zqW6Qyf=R= z?Zv$!c&^O)H)Lf95{RJWcK1OPYJ;-k?vrK(&sUE;JUk{!dTOh$_M7;Oxhy?{6AuYN zj(A&{IwlR+&-h>RL>9+~h`m%|os&8}a~_Unh%-vKZsrhoL;-DcLIU3yxUoHqP>-+{ za_}PR#d!rsSd+e-{Xu@LnLVToNsR2Apq=w?K-&_8K;AZqL(xo`_9YOmB7)^$GVrfnDp{r#M1>Z(ArvVOn^;m zXIbT&Ev6@Pd;tz*ww$ucx;y%%6?v;jVd!RJt~wtI&cIHfNPK^2Pp6Vr6uUT%P2B{( zS*4{_>{e|vQY1jPq@-1Z6>0()d=`PUNxI3P0GpW8;SR#=jPO<6mK-681{hz; z)TFg_Div%Fh8KHU;+c+hW|C%-K!!_&`~iw7=SjOCqyz}0fn_(wwWg#`NiOooBY=%$ zE|zBlIS$0dxC6&PRQ2GV+=S8RkLNtUZFR>1r)`Ra9;d3KLdube_Wg4YifLf_icNtI zf?C33y|wlSHdK#oZeG&i9e!4J$CXCPz8LStX;~HsLLWh-v`5la42);0BI^KZHw=5>)t{4=3 z&R<5kDR7&L#JUn`tqX4ow!Z{rFMlz8HH63mDh}QC68td<2dbJ-WSqP(_JSGyp|M0Dh*qU>#iEJOKh8Ua%WzX^|y) zk*@6ls}`Co;N=z7p&(0iz5@ycCR0WUhqVp65rRppZ!={r9JGTNWX5e zj{ra0$F~(^WTthmhMf!`jdS2?sga=XxiD@qm zNgGQyuSxG7Pz!-9Q=ON&UG^M!ouL~;jmU{*nT(Qq*km)F`i20Y=57nqH%u$eISEeL zs-z_w^R-09;Fh89W3UqJG2;MnM?B9*3y|nGNMVE6uC;7-s%q=H|HY_M%yKB2Zw)A! zYT-y_8@AiQQ){I)TbRhba4vfMGk>1;7oNNT?XmK{cEiS$K<@J#G!TvB&T+ZZ2L@o9 zV41n4xdrE7iV% zBKcrzFgW$X5u7`NrFTmS6|GjHQ=H_-J3rtLSq_SMu%6s}vc+Ig z-R+6=o7d)Q8bVR_1hoWHI*A8Z*F(@OQc`qd9hcM%PJX(57E2FMIzZVB>}oYf=Lfn;M5*YXnD{QO7uZXZPlyP( zt*a-Le7W3CZgZAvNiQz5FPU6o8#oJ?e=W-XxDIwiUdHy+mHQ&o1h_TeX44lfrKZHq zDNQ|zU>xOBB>VvV_Iv2Tg^q+wH6A~l2BkgN$PVdIl9J7~#sJ=|x9Z{!e23`gVNLDg zLoTl=#6Ov)x?<4%izgtM4c=}U*r5kdso`l@+zbOLv14nw5n5awEMS5SRrVUx#gIGI zdcR|7+*9ZfqUti61T2zmJL*fsNffIQ6}N<%Pob4X%y%9D974DUnG+H9{ymoYt@0j| zwgkg_B4!z&8k~OBEU9$Yz*!@`Y@FpQAdpSySTEkuM&zeqFJ^ksAI9`Jw#~a6S-`Du z=0u(A1!2utU)2g((4EcmMv=o;SuzY%G1}#6xpraGyFe4m0S<$3KE`{p49<4460ROh`c^GJIJtZ z%mx_OxBg*-oIzN}{-wGn5}^D)58P8-s|sY+m}_q zny#J`gCXGM`a_Oh@jN?%f#bTT`|CrJ@&#sBtsH1~h3`f-+Tn@xP#^l{F|o3%!xERJ5lKGeR;Ilfo@ zF=^M7wK}D$!OmtQ2q>C9$`cW`kL6vK(wPERz7Ak$>+&X@u5Omd&2!`H8M;PRH7!9m zBLoL12csRa+T#5l%fB`4AhWsa!@%c>Qhp-^4HJ`MZX61zE68E*uGg%@OfI?p^J%Fp z(dCyY&&GR5e>9Lu+sQj2OK8DbgnXjTrjfNfIvflY^)Mx&^@#86Vmd661|QY(#HpN$ z>qdxbByO{qxEIeE#noclFxL-0C#kjT z{s+y}g%`&&Z?fNFZpb{uC~RVqKErY~Py}e9OkphcxiMQgdjd&P3cWR9do=&P#q&nR zc*I;y7Zkxn9g#G++`w#UBOWvA^vEtU={~&nPg!&m5#P>NL>eQ3UevY1nT%^3ODCD1 zRL#nVihmL#LxSHoG=j=^J~RznbrNUZHlf?y_x4DsO4Dpwou_v&U7$)+rPg&}`3-1z zveW&#id?S8iLcogzt1R`{+05SCeHHf4C^c9OI;70^f>Hd8&sZpLbm5OtfYl%LGK9b zjZt#6N4YV-%Q6d0OQkdYHw~JrTvxQIcB*{+8lqiJ^y)&3u;0at=oE+4=WwOV50Ok| z)iwQaKzM-|1ve|SEq;Rs%7*)27X-i-aV5iue8FDzx``C9Y~0nXWy|>a1|im_VD}yQ z_ncAZA}h{I^Ml~TQR?2kT`o$IwAtz0bX(J+-QC31H^<+e8^15h%{2$r5G^9a(CC`F z*URo%l)~FYjJHuTI))ynu~@y^+;61 z=++J6sA#5VqC`g-qs^t$SAjLK@dHRAGwWe9Z7A*`h77T|(dWx1vVowr8dw(6HX`xW zs67kUT3HU87Jl*Z?paz?Wmhs-Rj7>UXstp(?$u#agTl|6xT90NbkG!_A(6Sq-I8C^ zk4q^2{)LWz%pX(%@<@vTiyS~lt&$(p?uRKG{*>+9yI_P0hE!?}Vt3ZW>;cDKdjn#J z(1Ngw@#oBe+p7trm zSW5=%40Kf&tR=tF%h=6x^hHRK+N5}OipzNrk5mI{+pJjcE)?yz#3DcqO-D6+OPVXm zP@Fo280D=mb0Tk>t9Gae8_9v*!n#Yk&^~n!`&IiRooaKRK1&&`?V#A_TQ8wKQ!zt; zQsp}$)T~jzNlG6{o4Nw9{^~yOcD;p??T-nS@axKEIUGk74@wEXTK~L34P>~S!8F=> zs7xTyD^H-5^kb<{Mi3x*<-OJvAhN}(fSVc~hVIe`oT;TD*&d3YJ7&kZ{+{QbaW>%w zU2i+2f^dI$e@^mR$+O52>&cqm+;ZAvpvUt`GF8bt7$_^xq#s0BA1*^FC$SR0kI6A_ znQ^@axPYoDMFlT%4qk;pn6MKhsGR#8;mHR&#owD$ZV!xoX#;%ogwKS+f2)&wup^)mJX_AsTkZL$pv=KPiR{G#A3LJ`k1sJ zs0FoI%4Le@7!EMh=WL67UoQ(xQ;&SxV|?7`SJwCcaPw%=Q084Iq_#b2GprU>vo^lY zV){xsL_Czpof_iYjdir#4X)^1s+AY+U?w@08rPJEw~2z!j-z?l%Ms2rpR8BC5V3l+ zJk;pi>JKKz<368PMM_Q~8H=2qTqogIXZ^#=l-R`Pn*(UiVSQ-49x|nF8ER|WvKh4v ztpQV9fgg1UQoW;_=+OxEd#&a75t3Ra9r%!UZd7K4t=7WSlB`--vQu5;QwE2okNf&z?Ox}wq0FawrzH~ z%eHOnFWa_l+qP}nc1^#S%}m7G<{~q4lacq_bHBsN`B`D7DUe6F%*&QyZBLBLe#@?e zgnDI9z5GF1wn*BCRx}De4$N@zZITIzQT%-CMRvOclP|0#6fGlKsf||(X9i_)e#I*C zru*<9_^VZkDfFV33!K-nt^&=FHp9|0492DO?$BIwwg{+yP{Xfy z9V~OO`f^KTZm;%_@l`NVZZOY-2NXwZi_okUx3cW30X`ZLpiI_^vv;2KW{yL^_8`J$ z>8XUFXkxmqGkT`8W*dDJd2kOxset+|8Z>wpgH&UMzuE7J`0W{EUuP}0QSa(Mbw9}; z$*-;v$J{WAJh|LogYw*qnTGEY>p|pKQP<{FI@YV( z8{?2m!HsP^OW}GROU~JfW(s)qB!OxEQwPp)%4pU1OM8=cm_ir)1QtnvwovE8spIdF z81b%5n#(A04}r{&$Cq_kwR0BV&S9!le%&qDi$#MxEc_Dv-ZHVne-C>2>$`bxt-wpr zi6xkc2nKQhwFHnSQ5wSUq}JkdxhJr>81;meSm+lssKL_#Pv1b<2pFHsGYq}r{9s<5 zqosvNy(K$WnyUOsNNyZ}_>}d7ibRk{Tu1fGO485z%6hr*erCATd`0ut{94+5jI?7>)HlyP?WJ#o=7=czjeD$DoEHRVX8Yc&}$&@x7b zKF&%~s6!8sbu6bJKAdF{3yH zSXeDUu9SR2Z<>+?DeKW z)3zsU$R7}awDRB&g>reT9U!6+5tSRh+w-8^QVVoum>Kak*To#EMJi%o9XvZ_p|m(y zwNDILN%Qv>2r}i>xe*jOP`B6+8+~xjOz_WBd%LlS43TxHytjj~{WV-a z@qGY5ab$Az{w4h@w)vcN!OC~mPzrvxSrWcnnDb*Ig|5t|#K99@nrq}RmW{MzE>Z(! z!ev=7*vld)Px<(z#R#|xe<}H`BU)?nLqP@7=*QZDO69*>vr4*6?uUhe0;3p6zS`tF zI@dJAG2(uH=UlOsXMrV?Wf+r>!{z@1MXm%OM(fLE@InUKCMHTksD2pq*_L*tP{8e7 zUl<5ow6Mr%lP+x7(x#t(`p@z^J2fJ)Jmit{IHsi2ASplb+<%tmcJuBR5Q*o34C}l5 z#s3~D=P<7?Fz@7z8bT2Jq>60)>1kcZcX#oPtn%#b@}XL+Hfa}*Gl5k3^QzABO=1>Gk>SgW8i(n^$UceD1&S62<-3ttJyKCoFm{}wymX^6)z8OC} zQE%qK%vKa3CU8J{IkSzRQu)#?CTCMp$?mkbD35!GrHATg?p!-phkvTUT+0Pnfzihk ziUr*&M`rTh|0adWFc>Iqbx{Qa{Ek&auUz%&9!>DFNa9ukx5c<1BpVn3a67$mBkUi` ztARyzHHF&3NW1tjzPu@4%fSn&#RF0k5|QBMT-&I2IYfBak>{8Ya_ZvobRq}7Q6@?iiFsL+yc&b@G__5$b=|(N(D&j`;Q3-goNAFQJ^YAB0$PQJVr(jwq@O1dcvE zB03vf695>taOW!7XAN#_$9!F~2mXpKVJ1V6pCJ#gh^Ke1P)9wL8r{Kw1>PT>gpnG+ zJ3sghXUu=A?h^zrNde3(>>U5s-?N3+7ERp2_@a6WDbk?E@GodSpApS};z8*bUI} zqiAf87s*?Q_uC}Hw{Cy(pBz_EN`E+{F{2C^V@<2Dl)GC4f-jQ-`{W5%mVz#Af(2b`h(w&BW0xA1T7v1ft+hQ>$;68Spr z*?#e+kB7#O)vxb~S+%;Id?1uVUK{`lh9-Er+lZGjfzRyEqkLgD#C%ANWzvb)p*z)7 z-OXd%+slXk-yQXQHHS@N)#>@u482c_U|ib57ft95+IQV;=;A&M5zF-fuOJlZvxs#v;)q4Q*`ltc_ z5$~r~&7;>RoNTJE=ANlQUSqqU;9lw#iFc-jMKRyC^6CRMKj8o{n)aOL9*6iJDz7Sp z0x9XxyV(tGS(m1H)Osa#h5f0)v0OCsy0&GS1=p*ECT{6_gCuez(xZ5$)~Q+)%9NE8 zpbg@QZ+vf$hWWEHaQjPmA25V2w?4Gl2Z(q!4ukUf{e6&!v1jPrtRo zq0f4mj*Jv-?*fU@>4icljizMy}%@W`e4+DUL+vSBpQ_ojPe< zLl9;6;*Fb;f60Xw9yPt*4ext5Q~^Rec$s#s_&az|(tv4pN8aGDXj@v!80mXj`I<LJ&gqS(?@#wrspMTtsqgS6C_;sn zGlS^Zsx?kC{e&ncRyg7*zE1s{Q&kQ=@064Qa>g?nx>1Uyn{F6}`_8UtFWqkrw(R?; z&n$`D$4OnwKjB3eQ)@DCe*WZpHjWyfD2Bknx(7^^X(0eQGibRcDDB;@Uj%KatJYI? zA_|Sy%i~nyADWi}y8Do5F{PrBsAHe&sGWj$VVZ=wj6})!RKKAQdABH#AXaQshiuOV zO1dg#FkJDNOR!&)Cx4B(kap=@EN?N=-M1xTWOZjn*b}x@DfXw&fAp_3Y;0R2Cue-I zMC1lRa@zoYnnXD+y?F>YRR3sHuB?6BLx-z&-7k3k?(^l_)~NR_#SVC@SERlB1sA5w zOdHlqopV2D;K1fm2b4AgDYoA;-HYJY-7j539(&2k2x6&ZQ{!(Y$wFIM-7(TAAe9iM zPo{#FFD*ek0mU4^e`aTrsWqL?#7bRLTd5|h=Z6dc+GrBnj8__2jGN>VWic=*d_KTT@c1sPD4ZDHMzfq}l(S_~%?neNf0dPRy!Aq@uk4h($x^ts*? zu?TIEA$B4DI)S50rKc@p&9YarD>r$}KL(F-*i8fRLgD!}y1IXnm$UH^qmAC)xF>>W zIS>D18auy^UQ@*3Tu}K$pHTH|9Bf`zSFuWd^d|5Vbkp6+toV)d?+7|ID? zX$a~go{4k{nUkhuq(60Mgjczu4mimszCPTS!0yCR%7c>O7`BMCR5a$AVBbn?=KoR& zAn1#Aca{8XU090bd4uH%4pX7h@(?`68!HVH@0Lj-73&`(z}Smp-;(JeX5Xy(I{EQ!7LZ7LFMk>*6Qpe(RwkrH1R9wy$IJqzzjCygfnioRbd zZQ#ofQFm?v0j(aXyRkp-uOs9i{Eg8WDVh_%n_=#Z@aICRNH-56D9R(5hVy$vm2N5M*v8;t6H z@kDbvbt$tA*={()_ii>fkSy8A)r?Xo$8W|bg-pW~>9U5MX3NT`8d!0XM3OeM&|&%* zo7vEnYo%Su$^<%DDIy$yg`D9AK9WpsW6{R`mIXqMup6)Ik6l|zmgT0iRX6F@UpQ!J`oL-NBH6P`bWj-Gpn8B6301kg<>8>e} zL%GI0)>ohfCvUVN%|z>J{Joh!-b1rcWKtqjiJ~gYckkRlmDU)KjnL?9oGP6HxzMJor}S1!dH+s zxzNsRnB-vh_*_(B`+A+KZE9}{3E7l8>aLvj7ae;;Rbf9} znACV#63S@ZZf|Wth_;?C!Z~B>!vvL~(0@AWkWpWQmG?m?{+HJ50n>Sx0!P_UOv5D8 z(j7DqF%oT$7a0fJZ2x{u=u%SS-QcJI@}v)c5*LB30{VRe!1$n=+0UH=l0pgw0|(N_ zy}b}_%varhS9ax9ah}u^>=ga9i9`clIt(g7`;~^d`>EGl-*cqF5lMe> z=Ui2QhN`i-Viw`gmSR|m2@%>p#bx`bYT4)c9MD9)N!zX*!tVlEgA4?hJ7GB82wHa* zzR^~#(bk+)YcP76TFd6_eu9sS1o(ZNB7PUqEg2GA01SoeK}st;p7yw%1=};^44ZwW zAIX4)%ZDIpym;}(5GvV>edXS`D>MiV$f7^CL`jua!pg(30+tVi%Aq=~D_*i%pZzg< zvUU-z!GtaKB8P!yc#k%%T?1Bp83ga|)hzA}8iQW)Sv%6Q9JlYQ{IW65+_XH-WhQ>U zX8LL*00%8WUDwbaZ<2hHq*~KGy7#Mj^u?uFLg_uOc~V-QhC!7_{qf_Eei)j*1<_=r zm5jMgOVn>;WwekDVA0s((GL(7FQ{0s(JOFASclWQR>yIon;%3XQhCzy@r5k{*QBEr zEVuP)(chFd`4s23JpnvWujGh2_?;1op&<(ZL<5+XS+!BrO zYX$%Cx)l7TPT)otXd8;K(=q=>%57WX1sbRK=Z^mjW_7Cxuw%PJP#ckz85TlrPIk7{ z2lPG{2>RJ5gzv*osDs#Z6)c8B@()ILV|+|C3!+P{ISe%d-Ui`5RDO^Ub?5#a$WBpwr>qxJP zD*RUgcRM5w;44akhs^#4S~ranx)aAV?zIaop^#>tc=|ZMEqbo}PDbh1Q$zBvZ9dtv zqAAy0^wO=6+ke>E<4QS8kn0RomDYGa)yuprbHx#bU#_ONyRy9AoC_%+eBXR^@h9!+ z{hN5#r2QM8AyP~p{;lRcJM}X`@7)Q{5D3SWf-F%EfFSx!;&kUqQ~zK{7J!-oAL7)D zsPXa%grw1fkF*VfbdDk2*5JdKTjz~8G=2^gObVxOf?b+pY^qM4V`w&(8DHi^|7KhC zyA;&AK|>%(e~LF<9u|Ts(m+(-Nw9A!Ae_&KjPmVy+TwE2GXZ%lloV}`wjDmUe zL-4RRfXMLii$B#~l_4gP$pPZtdYT*9<6S0M1Y&H$OOS z2*VZJ;tbPOWdi+zPuf%f=)yk<{YubJp)JY&B&qN5Tn(# z^AR0Tl5)sl*SZ5d4UzQ>3NEfAsD;qE=Izlo;LeVyFMESq(TPxN2dKw_)aAN&QNDFm zr!V&gTpzX&EtMDxKN_u?Jub-E!wQU&U;>F|!a$h#cn)}xmwSC*;;e|A^LM<$X(0ol z?Q0>(QIY3Z!`(xzMt~p28kX}MFgJOLrIZ@nZhv1a&XXjcg`nx}y+ zsVFj+E$M4vSG)wX34BJ!cx8@2n)YDW3XQg940TZX_QW%jcxSknl;_`d681{^D!>2F z^pl#}K?Kr+w>%U6d9TBV^R7J%qPbcPh~mahT`C8+%Ai|J3hIyMN;zZvbkLrG+!&e+ z1S>$osBhDfB}f`(Wf^0%N?{F_f5Cb|*t_oc|A>4ftm=xmkTz@ZN7$f7l%~)G(Yta1 z0?YN)2D00x({BV5=LFI?W~k*cc17Ki`mEO3E0_?1f9GTZnB5Jh-^mqH3F|?_M*}i_$9e z4z6m|i6HG4z2&Az{YsY-Y(7|I273L#AaO7cb$fS!g$x04Q6h?^M(>KDuUC78qQAT7 zeh4+-kH{9qUG)W34sf8Rl2B4<6iI(sG!Lu4@Eak(>FYf*Q5 zJ*8#1lNFQP`Z0PN2UKqt-1oeSF|b+7gSttB11LtlGU&kVW=Vr9d9_Sy!X}aul@lbB-FyzPJdq{(gKgCz^26y&!1>;@8=#&4<286g`#wYJ z5Od1EqKk<3Wf~gAks}Rwqnq=dBJVj)u;PP8_?Ngl@`19jhVE;78se6O`b%Jz>{AG&NN&XI?kMp6zZ$*c1si14jFk0Li#8-vOM3uP^ z^n^|izxCBhO@xdHn;vU|oA8tgi|$1;&^Ka_4#(L8g6Sc)#i9I{8Gu1>fv%qulWilP zO>b%=-b%FV!%K4rT``tcDh>*9?-l)5Bke?CRDwW;lX!43qaA`V+KEAYU9=`Bac9Ap zlW<3{>gd(bW_SR#juIz;)K#%TVYCeb7++dQUb4!{AXkKggejM#ae?!>a-IWrzSsgI z)QX)FAv~MaV-5$vU)MM^dZ0&RTq>iULp6@Z;(#9G8aKu>(*&hxbv0>*h8-4LjOF%F zk_Lo#r@&vx!UJguTz9EvtQS4cw-8{SI}SS5F~xQ)fz2vdfK`?wO^fskfR__F?8@Y1 z&Om`AU4&eVje94k%wnb5RD_#~MAoVp=58!8og@!qGT#qa4z>?ux_y0*l+?^1Ss~#h zd)J@E4x!p9C{d--e{v*fc|I~I3+zRWgKFvvZCvUf-Gg#| zXEynuWy=cSU!V#SIXaQzITDqeMZx3eOu5PvWrkS1wwQt4L=%?F8CDXX;qP5x|Mg*?})Ua>(j6jF}gzG0_jUj~%=Z zfDHo7ewED02bJ?J%g(^7nsy2qfSR}}zR*9BGC>|;o-r$k=PMr-;bw7{#HEY+WT6=| zXjl8FD!1|(R`b~Ymhb?W6v9!i7IUN>S`%}(x6Py2Ss#&0H7doq zv(_owo;0F~%^3Opl^pvhsIL;0zY241KNmTUmhiA~368ZzQA#U2_0wsoZ0s)ARO%!_ zTNKw+r)Io^W(Di=DErKs=V&z#B&N-9T3Id$cL72D7x{;)sJ#_}tqRMkgXebJKVJ{< zS%I_vG_FB}9XwwXG9-aL!91NyGN-6&q$e-wVy`aol@ght@(9~zxNW?0)~c*sk{mBpwflb-*J@iS?o*=4W(}5gMOJiQ3JWqyWY)RGCd^ zD0Wzm6VaK=Y?xYVRq@#;sgKcC2%+TUaT0Rq1JbOb`Sn;I7V#HpM#y^3!;;#XytAeqI z*1i1FpQGRoNGvB=$GID2_RK5*Uy&7AsJWC^8i^(Iwn9iJAW=WZ735SjNv?duR_0FA zyuTnTvE=-@m#SmyW0LzkVw%g+ED6eBxvf;AzZ{<%x4=6=U$QKH!saZuv{6iLG{q+k z$TsRjgO>Ke#8G!=bTWE0O6mbbRcS=wRlRlR)_OnWYTGAsV;cu8$ONmdEOF>oYWYkKooJwtfu_k`C*?KNsM*YpZFN;Ik>3sa=nZhZ)nw}$R|ooFLxr&yWdCj5Ry=7K0IpcFs5 zuq>u*J8o>&8^76utzD30|HF$L*cYLq*!-vfx3iwa5P2NQk%L=o)dbB@h2j%xP5SSf zv#!$b**4Z3@5v*MMGd#kVK)78L>vMhj56^2VMAe>272qeVFLm%vx}T_4z)?q#6*X4 z-uWR`e){Fyf*VwnoC}jO->9!sGdvVT3Wj_9G+(BO=Ra5B!6B!Zirs;(-7?X5gca2a zKS8DC`>`JMw6slVzk+=gSI;Ev5u%`?FJBA2s92pj~$h##H_acyyQ#2IUvmK{d1kDNHWj0VZOix#hO0Z zw3iIzn^1D;Z5G~sU`xNj@K+cM=)*Rp4nug=c|eatl3EfWZ_be;B%a0y%9ivzuyzu5 ztmZu_bL;Ic=eR&udLOt5L~#OE;;h2*noiX5*yypz#CaWHNNv-T1k@mY+}LA+JA`Ay zRAi|gXPXl}f-ECsS?tj8hZqrpbfVJ{=~Pe>sUNXOdkOD8{wtv3fmOo)gw;HXlJ3g~ zzFYQH@zD;FWuiWpmL|MjmU-cb#X_Uzue*0@W4gkt^3-HeWrUZp>}SnXUDMO1h|EFp zK}D2}M^7yP0KzWoX}FzZ_iSPkt(xEve)T*m(6~|H1Rhmioftmx8J~vv7HjIYamm$` zys_cL==!>?g;plKt(K4=+Bs!|e}b5mP$FeBchSUyLH;v&YNrrjju4YFwDo)Ez`*IRg9q+BI|riRNayjaC4$=Ss!=a zMgqQpF0^Jvn4bTDBhbOjKiUjV$^i4A$w~=Oym75EDLXh1r?4vTD$pYp5abxu`|Q=h&hE= znvBHoW@*4+nQ=W`VYz$ujWA$QNK&ujmQ*IdVb_E3M=^r7N@WYcvN$$bT-P2da=mql8q_RcFhHI|E% zkF+wq!0b+qSzu5FwM^Gzyt4smyMinL@g+8#Q!G!u>WHCWouvo z!^4x(p$&`+U}58A{(s$lZ7EyB4*2aC)mOwrr5i{jH~HKa0oRsP?K#n5`}Xtw?VlbB z%DDJaN1g-@pTPh@raO0WG|@Oh!gyD}<)dsLe^-PFrm&8}HvV^uqLd`Ck7-m-=JwYG z;e{pO8&fCng8LOvx7KTW!u$C=y#l~I{-*-%bYFeHXKWpjLZZ6tY`b`bN!~~YhAdof zT=e85Iqd9>P@(URtbZkhAy;=BeNyjv_WDKGr)RIiY?;c z5Pjd#R)R}JwEl_S>sk@M*W*7|HqZ68OV5ry7Gb(M{#e@R-!)`&uzSaOG`Obc)WYwkp}u0Jf#$}TLG!@ z@#`u|3TmVT0`ya^54Z_&0O0ChdK9i4fNdN&|1q)e?7fB8=^Yt z24TqPx0sHh#-~68WT`KX+U$Nz%LP9u{pKRSfi!$4lo^Ed*M_e;1E5uI=iIsG;_v8F zy6aK=SxXu?@$;9h7*k8KpwWvxzZKZdXfh*TfoxQ+%L zp-;4jnvxVN5aRgmU>%gkO+oswZ$NQMcQ}H>uEuJbd&{CAfl6>zTT4-ZR%uS0=CcbP z(>M+AwYEU#b!7mLG{E`&NeKxmw&}6}N#qazkk9HFf0_C)U1qf(RSvWF{eT6h;^=A+ z9`}7q!X4c2YckpL>X|vB+iou5Q-_>`udbMS+*5AQbCYz1pZC@S{U^?7$R;CuqcL+@ z{dmZ1=6ltlM0aF1pT#_EcmQZEx}orq@^FY0-HP!EqI2cRFo1Bnh~AgqIodq#ZT#i1vdrv8IjHOddCZ}TGqtX=hjbOMpg+5RdU6)EGTm$gSexu zDyjvjT%*@0g4O%ywYY*3Cr+aeZeeHf5}hNW8#^aj)UA|pK7_tl{(HDTLT2_?P#-15 zFP|H3A{wO!2tbzOiPzOH(Y>xw&uZrznogUGD=^Vj!VFvx@11xi4S^BUHSL6;=umu7 zOdD%HAVP+M#M^ampkI|jH;8lGA$1t8&R)YIJP5Z(94A4EYP%I``hHd!e)@pJjU<;|1 zF(1n$3cX>VB=@I_hb{z^gM4{2ZXh!@-tr6qno^?oE*LBj+`jp=2dsdQeQFi2%ad`* z0(4vIvoTzU-HL{#Q$d3M2~z#K4usL0=pqVG__|c(O0Ej(1cR3%l$%=al_C;pfQ61F zl;(2M05IY07ZPcplV~l{Cf6c@7SQ$uCjQF>yyA8=a^;lh2i<)3P`PH7R-SJ(G3@2R zhU?b4;l%dHARlX!YEiT}aC8!t090?>sAyIo$+g=wPN~xn_f7wjkr1%0`uZr{5+Btu z)mZmyXxO%DvnF0a_Me!F4%TOMs$PTUYNtC20?79&d}P2tQZlGN!-E~Qu~E1GEECVE zP=%!vsdSV0qB`x0@Igo09A@1}?FK_!oq0HLQj%!)g2o-Y-uXI?{oWArKN8v*rdHhNA2#aWVbK2$+Y0oFC*)_y%Rh=ppY52^XU>cFcly9O|99E*TBCuN zJEk_eGaFRv-%xexb#K(_;?dDMT1b_CJx#ilNpio3~t5n^r=@32~B&T?S=Gw7300!_I zTE`|b54{&%fkty$%wLB2D1lQS5kbikz!}a`_wFtS!cN_}m?Q?^!CyAi|NeylcuY>! z*fn8Aq_A9}yj|FOc%jOvg5(wihYRpEf)9*~$|43-yXA?AyztRx);3!9(?as=InVDd`>W??uRBc@-!1D$=M2a?l z^m}4No~<9FIJ(<2PNBxJuY8OF#s-7R_ZP)gmy0!7u5)Paf^GcJ3xhQa+CBwRs<7V1L>gA*1h)1yh+=)VaIN4| z^^^vly<%0-+4&46TkGFS@c*df819QVDwU}fRWDH#iEftdIk3*)TSdkJTFYLd3tI^c z2d{wMxDXFp8eDb8^NUNS{h?p@3Q2dDLqp2^g)-7Tn7;n~vy^?7!Jp2(KZv6ZbOSLo zQ|YTa6m=Y!_&IK-#a61(kVWSj=lEXcO>{8p%2=)>a2Fx%hs@)>v<$Ay^5(lsz_&fm zJ5*ktdiiajFi=?esx#DLHxlQ3-DZ13uzIL|lSGDX@(2MH(|V$l*r$;pqnN5g_FBv)I|y{K3WP>*f`(0H1rryO~%7mzW4v$X6B zjV-mLxf4d9w z@w>&nJKei4GjxPGBU3$-l6>1_*MC_G%C(jbM=tlztc^7U^FxAgA)SdTLqyuKEaWrd z0~TUdV$vTfz~(wo@_JDpsr9V3C-7E_(YF7?e#c}^FjsZ!(>Z<;8b3H!xd03+&Jxq5 z`2deDxbhXGvUbh>>86{PV_U-V{HGBwSDn5}OMopWA@hI=vwwd`+4}NM&Rv!vP^ED! zi!b(Y_8Iqq%_JOP+@x_%+L^t#g8Dt3n%7@EX|9MA(3jo8n#e4!?ZQeFe7SKX05VQede6NhLckCklNm2L_*ag+(s$-BvMxG0$lQadmnI!E{mL zm-ZwH@NQu0HqR!4TAY<2H5O?A7h5q|*u1PABqPzxR^=JI<0a?muBejcc_QV!wdnl- z2R{r>f@ay&H`X{B??V)bRd4+3sP`=T>aMDm26T~ZwJF^jBMg^N8P(e!-plZWPV{hr zVzlB87%K=pS7p)wN|2T*f{QR#qma4PM^*^~SgZi=8V__AVjR{QgX7inDP`zN(Yyy{ zeALlB>a=tX^oqP_(By4L6HhNmUIT~Oh)$yS+T%|T-Pgjha zg2gIBDHSdoBN@{2w=$}o@1YQuW2wpOx|^N#urM!}w|*%!t%1nGwTU}E2X!2)NRIw+ zY_D;E`(WT~p0IKG-c)UCo4UuF*QSmxm-yoh&RRf!I9{CD!j|1vlVF({Y4b%e9M-K7 zL1^gWX&$S|O~X2IeH>~<4~wBjvGOpY z*0NWoVl6=4`^b9D9=Yp{mo8eBnx&IC)>oYkS4Y!hj7!s*<#BQJ-c0eKQ@6n{MlFPKIy%+g1y0HD2OG8sDTLpJ)xI5}@_r2H&l8{^`(Z{=-U8>< zg0Mj5X|&|imElqH3uR!)l?(*{z(F}}0->p$c(C>8U4yZ%pytDwq%wUGt`?{=v-hd~ zQJ%FHY3(m_PQq622SwC)hs;6#L~;}hhlfe~$ry!Q*wwbh%PNjT$ICiByw3-t^QpM@ zu+eJw6BY@0h6Dkpw*Y_!9wQTTN8i)kiJHn@Q{h{$$#mFhA3VNhV02BvgY&cLTU?Zq0>BO{3#Lb zE!4r#h}V3Sr86s*wF|+Ou+Y4{ua(Sd`FkXZOXr5;0_T+DUK3TL(alqztwvxy$}3T3 zb3$)3gvSc7)iCpvg4YS~C4nDLGUxP4;DU7NNQPR+NOB?qwb4aT{YS z==U>r5QiGh-a(3H8tj4)bkz;MQPrB_oiI|7-`8iNJXAogX&&o$_63i>vxfp0ecLv4 zqzf0cx(pVJZGEWvQ8wd4F1Jdkve{7l0^1<`9htd>$G%hZEEQDX7Q*%#i<2q~xK{2U z6!q;2x(^>icx}}HeMB{6lkm_V3U~IgeWvAS00$i3L{>*QhMNX{WH;+=PZhPv3<^VY9iB_~A1AjSNLC(5own zHOh>?7UH5(oSpCcUle!BJLVE&U{5pJ$`lAPme35Q$`7yr?~8#R9NgYV0_eq^MsL%j z(mELNdwEYN>EL9zyPh=mncOf`AEZ_Oq572|xaVnCd77ztRB1(yeO-DGwMzSX8cLoc z6uxI+BhHvKlBhqR(!n0uDu^q$xlsOm_$- zBOt!YW-h(}q!q6WVg53VgK;n%<-flR2pC3x)Bvv^MFBMxem$-lUO!HkO_Z0wDdufr zoF;5~fPEIamSx#Yl{G5(2t6E`5ei{rb;G`w?O(||tA6%P3c1~;I>zC}&J>{#ifH=C zMNvbCm(OCp0-~F4t;9G=H|wtPf7N|e^d#bV5&n5b)P#NH)*od-k5{(gmo^1Y6!eH$ z%e*-iwIk?gCfQ*cFAq+3uDYMPu4Obmuq({pD%(|m!v#I|7x-Qn?kg{sR`+mZZ&|qr zZ$7kJ_-8w5au>;}%s@g`Y__<)U-(qI)aqEPt$Cnhi@ccf7j`xEGgmzbB|P^kE{u-G91!SECL1zTaUJ>{ zX(vNLJ-8xyZT{&c-&OlQvVk%qT|9df3H?d{8tqfmjP!IV8P71CmGhT^30qqGu@v4} z79?sVcvnXaD^RC{W0hI+*l34^Fcq+=b09=O{UT+KMhI?4g{(6C+?d1jY?9T8rLeHp zXqHA`DxsyXmWszqx90lGukm!#*ZRJx81JFxEmEbJ0+r}p|M%gm0x}T46vxN}Rb^tX zOPS;kdp;?ADqG=@(>;hXo(0T)jN@-GOokHdsc^(l3%LR`C)87FH0!QoacXdYWHSK& zXW|Dq&~;nmzi3sQj0wo#qyP>^*8eL|U1>?#61O3DpQt_(rXq~(qcS)_#nQxXkadG| zuGV*^TC61HN%-^x;+307u{MaTTZz@(8xi6JHBG*MYT? z;-BP;$UQl?{Wmzb4|~6+@V{JMU%qxydbR)EIDEP3Lc*oaP^K&+r@8DQ9S%@;&l+nM#i)3MqNykxg57r0VXILbTkP0 zAAFHyfMEhSZ)Hf)MXqD!!oLN#4~f;I&|_M)MM)Z;+U+?SGx5jgj*}bplL3RVr>sQ z=G`T=SH4hflV$QiV4|GmNT)_D4#~~^U8r3$gP;&o%!PKsxPA36d{rK$t?5y{E6H@- zZ;{4t$QV0K@2b;mrYy3IeS(8hASLvF8Db#asL4CJYLLen2!V38iP7DR6yK+JkzwFvP#cov!wUpDf|xn zrlQ=qrM~uO3&JBJ5VKZg+o*BkeZr=>I)BNwU4_jwdf=kpGJ}x@X_OJ&t!j~#b|);W z2c<6J8SNT@NKV%h6>kHTl8&FQHlws~0^Z{u)gBb*_Z@A5tF@?-w7AS<3E$pE*DQE6 zim^wk9aE|t&D8Eqf^+C`TJr5LR9M!l{Srif`!i{`>d`$wA*n=RyYhn_GX#HWVIY&k z!7Qc!(>m7vvk4DVhb$q^rxTZDoCBrP0 z@cNG{i4}_rtdGz$Ids`9yM6n(_^^$jO6ZfwDIJ>{9&q{Gpyu)s5Kc9_JS^C z`-wt|B{))M&|SM-64-vZTPXN;)#!H$)E^pY5yn1t4jjO+xE0al#R=RQ^ppNJb{ z_YY)Tx6n3o-^$0{G+mb@)C3S@#FlS5b)1C=g!hBzUchty<)A+oX0GhwQ+(2$lp57g z3IKJjyQ5ee#|JBL)LOoPAVitoA9fZ%ja1Zc88)IlDD+<0T}9IMO15P*HKbJ36FMWg zoFnuakZ#O0_zsul1Ntj|2%Vz5^1RK^*0Cc#aS3=)Dk^!t&J007$-#8uHwr$(CZQGhY-{#az)vkK}!d|y7?aFroBk0GWYONs@?U3&^uFj?) zF?G&1l%|&`VBJW>_22y6*QXOHd=4{b=vJQsXG?#atfX_I%e5$AsQy#rggqo2 z-GuHWs&~9+>~TWWjUM(S7WPXGP-U}dlL@W1afx7InW>N5+3I80vUOQG&z7X9wsV3@ zfF~t+&7bR==@pr`Pj{7h98P|G(v=>bb~}_d2&Y?z=XUWum^U6Q0FCXlo+B=wJ;|ld zUBtBNSQTzq#3-afiVuBHVT@IL+@`wjN2=Su*#`FtBZKo&*K29H*qy2m@Z|l?4}N%6 ze~SBR(O-UkG%rI1{CaRDv(8m_2<)z30GN<S06n!yiV;9XJVM)6)>zy}Dp z&Wm$sU7}`F{HNb9I$-s=L~bPNqH9dF!tPLG11t5o8`H{Y+P}D418GO(uT|GjX{cIk zCMCHhfX*8pdrof(p^#VC_IHh(f7CEAcY8i`{mvS$IRAsPq*ZLw>Ua)?aq6yv-&f^% z?s3lYV)2#&PT0nH_hjIaRk`b{fa9)L^mmJWH*bcaL5QwkU^yyh`O;Nq@F24W`tAfp z1LxpZl*jo;O$GcVBeW3iBc~ZQ&9Zl!EwK{Pb@2@k{blv+8kTLbE!TRAS!|Qjmo=>S{=R(XmhPx#-qgj` zIA+@QN(k{y6?m2yR8<8BU^;gQ8_w=&P1ut=KtQ$)8)6<0z?G+5fo~APeBsm#h0deJ z%)_wISrLvDpaKWr#9x#>4-=*F9^>N0elfxE(FHmvhLIpp`5{zgW8A1;sYv3lmc-{6 zF(B-+$I4b027nz)>_Feej5oj4x2v!TDI?(4H`hm-736>VKi97T3RY${NyKSDoQ=Sd(e$#_AbRcVxO|z`hGimD3Xr$iQ0C0h57C*-Os&NKqFuA?qNkzJ#VEHC=Ym(t$$AsQsAuS}sm!F8DyE{XGBV3!_LxF`vb8|5I}Pfy9BQ zAdVtDB_@Tl+A-gqy71%_e!zNv3Fvc z(6_UT`|Y}W3J7~6=DRC7Ss?f^l(&%HeqDIjkhBv#OMvF!$j;XLaZnq_DZ0yHV*m6S zA^60h@zteY&wA*Z;LrJSeSdgxtF%iHhq;shevMFlwRk9h1wFqv7V4$mD^HYuO}N>7 zy&>U6bh_P(3wHFGH(*{DG}}Ve_1{gqXU`wo!IstgcS#q+$Fd)wJQpz!>z$cr_RVCa z1!0{3^(M=4$q;O z2+hU-0;r_gE5w3(Rc`l&SR{Kpn&@%gP%omMbhQq4EO~@RyJ|1~T2i)d;PaC0X*3{b zvxc+APjas_=sW!U(|wafsv~kKES4I}hIq&Dp;*#G5k_ZCxjsTVCl{XzB7^$6(rqI_ z6PER{YB?OB~nWv@$dtoHo>GnS94Y)!*Da#z8wsTIQA&t}5P5nqIVXAZb zlu*D(Nui0k6mijAzU-{*)*+06IyTgH0J9-DRvg#6R=R#r_F;SwOXL#gkG+l}1x}Hn zk?D!UN|*tm%od6WCTbwL)ggb?Mim=^_~`hu1Ce}nscuv{j{3m(1)B+mi`m>y=``bfiAo%ym{%uKs}1x8KX!8?;yD4A%&uVMRe&q<+k zTooe;ockt`oe5rHxvdH=zA9~aDU@AXRrMDsB+V-4>`ytk2E;H` zBmeA5ZLGs+kp7kAXa>C=gAUXtr}1-@d&oRmS&uFG&hm-@}Q2PBn>?U|}36tJ>X*A;e4HAsr z^rcwTLyTVxH$dUW@^)&#l1C=c5Nykf4qub>AyHLjNr>D$7&|}-v8X)#_m%!4fXm2| zl-M94awKZDxkO6d-OTdXIUKD>X(Mbqv*whECC=KHz?Hq=%kTdM?C-cA&-ERZZ~&`` zWgvh?uyDgrKBVrdDF8>M-L)fsg62+f6lx5p8@r_v-ISD8Hz1Z*e+Wcd6vqoIqL?J4 zJ{9Rw(B#@N4DCm6l?6&#TYN$qYC%RBYEjx@P-&1=sP8IKpw~&*=H%T_nyJc=kGnOP z=VEOo;o`KUkunhiH0w3$TZ9&@R-(riCjTIv`_JLmmgsiix>Gr($^ zfguu<$<7GL&ay8&WyE|lv(=;Zm*DN!jxf}+AZduR8IU>;;8p)3(C^!x;9cG8<_|M| zAEI--kDl)hBkWC;>`-))EuG}k39>pOr;+McB8zBa2PJZudVdR(n9fT*xA4xLMoM zX$_EG4AWhh!mA*H2@Tt6tWzes}-AA4oOY$CK)Pc69)%DZ>h+{-XwVvS@=;nX~Z#>4%PbjKZ zd5e%tD4#{~VEcnw+em6Nv%L9tv$U5@IEu^YQQuRp=3%T3yyt0iV!Z8jcu-qjdZ}|3 zENj?@*pNd%^4EN^;S;!Sae^%JbiMnX{+J4H3YPJS2muhHikTdHyhmY$W@2f^1mrqA zo_s|pU}j8`f zABsDv5*x~D_BeImw0MOj9~Ltdxw7Kq4hH7~7Aty&_p*or-l=wo~Z=mNMf znbJ9vBNdtO3~0IZKf`V)SORy%`A>efQJsLcCS^>2sxz+BsbWs5U$zbox17?rML2&< zL)q-E^yf8e?a|a+Yurchr*ti-%g)8KSfyE&;{F3%$H)6#3#%dq>H5Eoj*jYGM&bW-~`I`Egr*>T` zU4kZb;g%xc6vrc-0vogx4YLOAT)(N&^Dhk^cEvljRO`tou5n?8HBku*SgHMB8Gtzy z-o1l_1pearNDE(6Gu~rv4FpAP0*Ln>e8YnzVw1Fzjge1Tf(kaU<(#!+Es(^!HTzj? zM=wzvWc+ZcytJZ_T%J#u0s@Uhb=mk_QV>E9jMtrVz?z#%5o(7l=9<+22wG z*AR;&vqXVRe5X>{IpkyOsyiyy3%aJ`t7c+aH#c9(=T{r{8c)rw%ZN&50K9$LF-cK< zn}3a-llH}&Z-j_m1zMjMN%JaI=2uLij?RkDUCw}6lTlAL$>2BhISr9TnwhwXa70u|?zA=TSlrR5~`3YeC&Mab(5H>h4!R z=%OIUR zO{a54Eo@g31*gbXr;b|>Q`ahe=#|oowUzI|!DIH|X`IV@sNdcD9l%-Zl)G5lr2sTca^9QFU zMq>|WZ?JrLpHWv*z#iO*jKJ?y^Yh`qK4~;8u&yx8c4lP{rJ01$C~lbV*sDOY$K|Naku zhp_7AD&cm1!vFt=j{K^|+o~nk`t|NeFsfw3IjM9|S z&REP-EkK)wq}4N3-9n%8W3=ERnZOs0j^@NX@}ylqm$N+QEGe-B*6pn+Z|4yhL`|%p zZBM+22EMps1+>;_rwL2?;v`ZB`X8Vx`WOwfo)!#w2Ld0rtCZ>2j#u9wujehYS6d2z zFCJ}?J~T_kb6LvVg)aJfm8nKZsmgaEHZgi$P(xy{p+a1+6bagD#?9rBK!>m8f+iCD z?dlAi68P+=&N+Yss(&)56!mM0;7IU#%)raSiSI-R1jx;@sCKJS@{HG(bFOc9kQBR> zdY(NaoiP`Y{VIoT@8H(Lx7`$yk48f#fWp|(xBtz{Kq-v_{L$B6NKy&@N6Ou(!cD{+ zfA4lqQS(^+n3_-iTF2u0`=|jS3oNkZTm0GjCbt2mc%KP%w~92_~T8B zfe8~RP=nfq>?W?H0*qD7cIxS7NO$i}LV+6HUQC4TV8olov<-yPVWNTp<2)30% zTQi_M<{6V}aGH^;L0dPQtiLwN#& zBLFeR7^Uso48YyYzUDoO{rnLojZ36RD&KrqC1gIeRZj|!r{LpLN~HhkK8+avc_K@d zzO{%ryZ==0zSH`C>-yOuO#9s6N&#?R&;fpI93J?4^u9lj?k8&OB>`IZ|9bb{+K=do z-KnuSBNHh#KdqEUQS)y-Of#Qvyg!FC==VD#IvrXbu0JO!J-XlbM`n+RV<*VH93U4r zx%YY$UB?6!*XpQ3-Kh9dZHa*K1aiQ3ROt8ec4N1B6XmVKNd+6huH#fCOLTi6B+9eh zONBJ$&RlTg6kdk+3!ngwqGdcMu;hZ>+Nft=&wbz|`RDk9&p-<*P0iH6R~1Vj{TLKb zYlHVq-xI>rS{Pq_AY=MKd1$eVIrZ@c!>C%k+0?akl*`RVA5WdQ9X8KlV%2QyT<~aV zd(@W4shK_TGDGr$k86Lke#&;Pa^e`QlDy892@12BRZ`TG3`H$o{JtTg0VP?h7PWGlK9P^$6XX7gnN$XQ^Bj2-HgmbrE0xLy@K|vNO3eX&ZkHT zgZIx~Dkp4W3R%AZ%{h4g<$t8OI!dB27&SkwktyCKr@D6RaoT})IS^_haV51uGpe@o zG2_)fl^I8Z0(d|@3_mxz8D)nri}hjZ*=*GuAWU6|uW`YWOiM$>Us9WbE$0%pAi7Xm zydFhqJK37!as*%-wiz|>l)Xd0p9+AdqqYolcdB*XH}gJ~F!?W5h^cX@A}AHWxI<>^ zaDhl}tP(#gpfEeIDpvXpmQ%Uog%5;$chwh;=Or3g0~S{zJz~|>OV;uKV1cwO>3Ps7 zao@_dQ8nJjEZa4j+PMCi7=vPM&gZyazv!U$`O~GX6G%N$C7fsFc2KLh9{SG1UOf+0 zHx0ePl+X%3zV;zgG`(C8SNpldYC%^$aaKu?#g(aGV&f22Nhm>WZoS^fHU-geeeh9m zg#$Zb|rr+m~$19(@EHJW*KZa*`V~p+L<8;%z{b; z@YO%0Ol?8La{YBD`7R4mJCZVWHdCq%W}uYdmefYR*AB26j5tJ}W<@NEV3BxTH8#U8 zWd9S}?#Mx&yxV8wPqF!TIPN8`{=$I=(N$Oz+GVI&kYG;&MIB|AyMRWy>THs3ugHOL z6|mGRa7I3G2}@9~Dog?60$-(#BoXlFWTKGZ;-Gs!3c2!twtr1#1>g|V8 z3^WOs1vJ>cXuda>MOQUr*s3`+rNt|tRjB)J^pMFrWQY=LGW|j4MEW~vrg{RqV~)?h z-kG-+S&0)q?f0_xkaY}GbNiL`yk#~YB0z0}ZbjaE0rB%ep#D$II91^gRV6N}oXkI} z5qY5d5<170AuQ{cxT=9h)nu~48{7af{6vM;Kqd&x*)UBFl;tXN2>Y*VH;PP)`Pfm9 zIme=D2yE(}JBrmctGyPv+19T_AMtTksy;qfO`Q)=$p%?T3fFd7=F{xNjDnlv3V^&9 z_x!)sIxoC%>aP&72@0ZCP&l>#FI3hB!t^yoaoR6SV~_fB;p0tsUIQ)S0IbU*dY3Gj z#fHBmYQyG`9f?-Irm7l1L#;z3l6Nc>;SkKg=!BNGfR}}-yru|!>*NLweWwTp2v{nL-1zB%>owE#ykPbw!0Z*-TAw@LlDs`!B> zr^rpuoDA;_-DNsldS)7Prpd+GAA%qujTy7#vVMb@OR)!x#^WvVh6WY$+IWGxHN9dy zdoynK#ekW)uV1gTDqxL|5CRP0?*aqL*jSYK=>d{TZQZAr!*R8|OK`({A^>f3I;e)Z zvcBoQ(cPf?;QL_7XT{95fU44h)?8MT*!Vnviz^XsY)b+m>~!24SE~4g9M7;p^}yQo z(z*Ogl|M~Z))X75f#3xc3YeY}mM#KI6YrXCG7X1AuhX=+(QNmZh;Qjk z6hZCmys$fh!5JMpx*O-nD`y2KIieQ>Egt_zN-V=ERe`XT>t>q_`W~KAgDA|(VB;EL zJVvDS?}@oxxl?H9^A98vv%fSTDg1=9Y`?AN+6n$Pio=-YHNz7xegin5B%+-6-J4(~ zc5a>8)KfMuh3G-)2b{8u$jHSWh40#0$nV-1nTSTb*nH||u0uST5)+Mhn`VM6CjL&? zOvBIRP4Z~%(!q(NMl99Ne4|qiqA?lphe}K@U}hR~TjPV()Th0;FrWHP5thJFIWwz4 zqALL5`tY$XY37$q;{n9-j(kwtQy3h^irSk<8>2JPNHs8hJE{BFwXK62=Q-TwNa27oKgCQ`b)TJMQ4l9U@rz0s#EwS^Oiz;h$V1rHNq| zXWMRqhd@F1K!eF-Tbx7fog?+axf6JWNyb9jpq=|3ISkMoL8z3$2B1Lud3kv@sJn;H z$ors!60p=`v1S;3f}13~*yBc#$~kzedyU85$exbA>rFL8jN_R|UncEgfo)PkxjMc@ zVXAS;+saHg)BrtZdeez?5q==a?gbKGdRmW1>K~5OzEh@X%v#8vXGYs3=Xt9a^Dl_z z6H@WJ1S5kPuj@e`YsDc(?r`NSW#y;q!zlG$5gXkzyRkGnv7`I^(ZLE9N)uc&{0w$D zS!XldHo23yWvjNw@o{U#k{kPpDpR8U9i1C0tJXB_ zuqbvB&O`QE)e5bG=F$CM=ZG<3Oc=ublV;d?n_HoTd5a4+Jnozfya6Dk^*SW=t%A#$ zzV-3wHNYee<`uZ8^TMx>XNyk8oKaniy{8Yt7^iptm#T_No+7c9+=c;^X9Fc7DTt*W z#EfHp=Ig-8AKYzky6c6CR=dJo8nbK#OV+yOXPxk&b%8PsY_?-5w1o&bq#%|Qr=Wk` zZsg_@K^v;~6a#LvKe5~71FkvzFfM#7ZT3jMbbu(`%}4~JB21}sWuSO9eq!bfw5B}l z9+wDRYuebNlX5nBRclrTX^kPZYOAD-JSp-@lJW^c+W$z}$c zO)1aFQdh_8Zkl&TVviL?EPjfBaOW5U`8!2Is$XHNO>284 zw*c?N&%nQ)TxQ7M~F zEHa>?B**zrizBafPmD0u2hDx2d_L>vKc8}L{D$hB19EC8yt@;Ev`y?8C_qk&u|of`t6oe+YoTT0!&M+THht>)3bfS5iiX<5Gzlc#L!KW^qEjr}xsg2a(E{gp$jSGJ z%KcN}OViA(N#3--jWle8Rd*J05(eN`xIy~;o+_7W4D&5|>TR)+XHj^)LMIP& zPReO58@Ws8%@yz5cT6gB?w2O$PIRoqX-&Q<>l)ROtL}+yyJkDlDT+LQdI33j_G@s| zgn-v?KdZvs%T(d!CS0B5E8VL+5~sBI*>?~#7dX__e(w2yA{gcLJne?z5FIC*PhnE* z(O;gn+R3Cjif6&(Q!V2XoK*b00|yh6;eJo#lqh>cnP8V#%b{ZTWl=(wj?>X~cow)b zN_>D5_-Koz`iC4ZtC2{sIsiox8H=?c!p26gaU~m=NMl~B(KJu8b%5cO45)=(zHAn; zlHqQ((wL$3wV)@`3+!7plHbi4;h+vMD6uQz14XX90u=PgGx{LpW1VAP^?0t~zwq!a zoW{^%+rL%JiGjP1#BpMsW3+}8GZbCK{wMF{gXaf!;F!glXqbS(8bA`P^sIDTqRER= z-3ebw`)7Qq&SC+lm9fY;a|bKQk$rStmOTf6^$CKMJ>Ui>7MvdaY+e}`nh%@ZVO6~V z4voBJt$a7FopiXF*@u<}!opJp*7^iBpz`}_phLBW0CH)~2(y6M9r-MRXz#~=LXpUq zqhFTImMfC;J)5_m2!JSgxtYi2bZqYTz`M{36_c>QkeTQa-p(1TDn_r|e3W#XC~E(C zZrI^OgTv*hooF2mAEoIafS^|qRuMS(Of1WjmGXH8lZ8RIPB3RCnTwz0BIN4KPf*Km z`6q!r&qoN6U*+8)Z2zRUZdxcQfMCjh2hZ`zb6KiJJ&8w!55R}dXJJ(yWATzc*mjxa zZzr#S(8(29rWw{%8krK%r)uXY-besqGXi#s6o}7GdblW;>SOYJ4GU?q2db%DR*{O4 z+M>sIackQ13|T4=MEm=}=ij(TCZ&cK?vI9559c?fm!>YFQk1c=wY1wS)QOReKy4AX z#Of`=q6D+KE&$6{U5ckUZe7Gie>JwyH544Het@!Huaz40AQ|j%`yp-Io*{a%lqasIR z7hXvx>@WG2q3_YxC&Y?yY;*vhiXLf+_R5wI%krZv=58xhzt1BWteNE2h~o+4ONxlU z!Nvy!vSh*mrJLwr%jb7E&6^_B)d;7CR?NT3)k4UErK-Q8$&U9y2mSO8NggNM#gWY( zTecQ?2VkYg0OZsA5+;=G5xF@1&X+E5cHcwo$}+NCJmjTYM#xXIUX^%utL4ulO8vsk z`6>ZLZ%VuEGZ>D2+c)7zM11{FHr{gbQ{vZ_-0xIiA^QFbwun7Oo7yIywio=Qrd{BW zghw$&4k2xYDqlmhUm+m8ylSV6p0yDJF`Bo+$pAF%r(**utszt|?G+AX?3U<75LqbU&@D64$B(sDuQnj6i*$|T7brr<^t*a;oyoQjugBj9O6?!4_ z;dHI7aeoS5#7zg}9`;|X@P(894m!@vpCR8wD}3&J zKk8Ymnppf_6@~?vA>mHwKPn8ze>LB)a3`XT*7wb>Q2k5 zvCx6zYIk2apFLhzSC$sioJWyD@|0Aotv+5m^{%fzrfea;*JwLvOX>+sI==BBy8Ty1 zzPms6cYBD$UhUug znos6ym>j#?fSJC5kA?P0O=}S9PD~;MI;6-tbSx)~wt6Bkk9{pVhpU z))`El9(HxBQav+h*^nUB+ z!f|yp`mHbkJnL|XQok-5!$@JHihnHIvk(ZUwNuo;Em*drkCv^~)BLbk`HrUd#mx`X z@ZD@fz~wsOklOAYwifz+O+)bj?kYGKW6m02Vcc662@SF6S!&&gh%g)iNxIPAD73)j0;??|xM zb)m^K`r%zNjEVlIQ;4b~0c1C<5D4IoaPj9Wiy%v+XI@_{ZL8{qPy@z>r%;jH2-(`k z-qxvuv)ngOIdDH96b!(_Ko|>oQHDq1UoglAHBuc;{j~J)SBwbJ^4C4)z=AsYUv4;- zT!k^=+StrcM5&vHSG+|zjjy98YY=Xf=gb2p()d}hD>1@47|is^K-_eoN_{}OY#lAz zTGrIR8mg>!dEF>*&gGM#bW}>Vru8pMeQ0*vaw6r*psp=g`QsOMMe?6qM7ze*6mj=h zjM;$^UyT;x&$LJ|5e~l^TJhAIFb)w0_dt%9dn9aR|9}khPT<4|1@79$lLq0rQHv;W5(CKf?~31Wg*j?g(_g zF@t79EC`7c1)5*Qse0LXV$$C zFHYasVx*1^lSG`2OFQfz9l7k5JW<9aWU???NY58tUl5u2+NqHwKXqzU$`a@ei5)MX zZuO-nK2pR`gkKp*Kc&PVG{odM#vaeb@E%4jIQWfoK1&pAXG!e~Da)eecj208r>s7aWDbj6r+|YASw|hX+k_Pj?`7rQt zzjQ@X1$88~uOc1*Q~H2;nV)2D77&4xF)))4+DCs{q6_|&f`y6){{+1xj-0a~qisem z;*`vdm1SFDumD10TemeF4E8gd|7C>rIxV4%3h=?CYYNGC5{=4CuhyPaTy3*G8uw~3 zrG!G_^gq>Bp5s>iP9AeK&|f-cv_DfxUcGP79!ZIuqQexBzglV0KheG&u%O$%?>Wb5 z@9Kv1l<5tcLhzBDUy^gJP|V9a{dCyK0&!P3rt@@aL@aQBDGvWU=E^6~*ife@NTzw; z&Sf0Qny|zek2TsiXGIbSJP>CF!BdtR=r19K20f20+wn#Y@}APRA-f+5T1f*1a;K58 z6m8r@JsSaFRfN>&2?gh`3tfrM7PkzX`C6?|6x3S!oJZbB_br6!U_a~>_Is|V!1(j` zj8P}Sv+>cY1_M~@kA7*+$E==r+vuwiSdvD-TbVSfCSkP)uE^>|!cFU>-fmKy^4)kkaV(cU3r%pSR*r zKJWm~@{D{@QZ!k0*Jljjo4N{>&|zy$QU3q^T4>Og2%$XS_bAg(wCl&pW-ZQ);eZjbN@FFcA zD(I5K?UceNUs<^4ik^3sNdf^G)UXESG&J)q!1;6M*Ypi7schrAsE7C>V^@$g7Q@&bcEAiOcVQw z0<>t$y_7o`)M+{SMr@Ni@NjdW?p#HZ0g+O4nn$x6#$(Wc@{#Lfu$#g}d1DD-7&mJDrb*dA}0d|TzbvebrAgp1-o4kyTFyle1ZyR?N8QZ9CkaaZO1SGkU`fiyza@19eBJMDH|#goQ%j35`s=7c<4FJJk2@gwG^I8W+5ZxC`+QaJw{HAv zk4-7d_a@o&^NdD&1^M+B|6P&S21JUbbLGNKFIHY1- z-Q${!3Cy7zG7l~Hdko4mEWEC(yYRHj_BH%q=&42@T=>(@~CrEh^9M zq^QXNLzQR@w4IIk5?t+!&);X1F$=BQZ%iMnZ747lUm^^`P^Xv%uWu=Xe%yyI<_wCPLV_f-qAs9Bl zo(?+qPgEAVUgOwxtM9OFncwAwdzM_xv+@!E)e~2j_mM@i&Lq$lC*y_JvZXp&pKWfD zNa>Et2s(Vz-8(uZ?XoU6b+d2f&k*;5_VOd$*e@lw->N2(es}&+%k_JM{;aON&(QjX z-o*+Mv%e9Iwjm^(K%#YcM;Z(%U7MGU?uSY z1N{}o)a7iqD-q+K6ko<1TGiIx@{_22tmqcLVhn*c{VW`zXtoYHr8F*c^B_ZU1- zwQjj~84^EQHgSAI+o1MmmSY+&`qVmhWTI-UP*r{z#{#{obleYwCW6){)Zhr4^OBm5 zb8-|*31jR?TS}E4g%KQgP6t=}wDiq?^Jhy?M&&L+#z*X4&A4frE9n^L45=oMj*TIx z@Kgyzfr*G)E1}o{0kPOQh{x(!zEJiPM9m`XOCQE#SdH&+$pbNNo>kwL; zoT}tbrkeCOLjIlW;Gc2R`Bg>mxk|HHhs#$*_R@^pJm1WIoOXK?2$d{1apfO?Yeks8 z9Knv~ZYQx9a>r#(Xl~c~Xrjp45~W_ZCDq$q3Id0D^{yGEWbb`axdWnOh8eolS56m{ zPR$9kcnCQRQro;{*)8ML%4-$lxq-A2*|!n<>yBZ;<0S8isc0=4uRBb~q zFQBy?D5|(TO%+Nwot8$=C6f*S)RzU`(py)2259QpsR6lLwbCNo;L}z3Q+cRnI*1FP z9;;I9xtvUnb;fYSfGVXRHk#?NaV|=0^>^AWQd;NivT1zpH2dys+80ikCelS&a?vJk6_g=1&JOFT7P^$pk}wAI5^c4ts+elrt3e7ZTM^ z%am|q( zmTb>8-}beHrfW6LUOjb_WPDXmpKL{u&`ww}FS8Zry^=R~fx@`o5k+M@%I;~;)iM;n z!6t|$L}TK`zY*Q(>U7Sz&I)Z4GJfU@7mao}qrLNA4>F9l@4NW`WzOhcT$ra1PHqz7 zT4A!o#(&2zPr*R0NzgYC-AFNw$HY=UXjW`RUm4Is4!eU^;3lz-81$_+U6)3W3I_^2N31J#8lf9%7Obcm+u`o=bCuMdRTjNw$v9Pzy z8A)Rv`IKm)UOP?!V6G@$eD&?X<3bZ#v33mHF6i=-*xN=K~ms+U|*L8ee1P2^8VEN@fY|p&?CB1&g;nf&& zH9yiL=j9<2rlnFAIdO7uiiy(}r4=!T=B7se9dgkaRV76LDR$|_k_=L|$kN6LQu$XV zTz{Tz<)RES5@2S$UMbv!@bKuztVRd9j-KQwx`nOT&vzM7du|I|v&Lgt6Cs_;Gs1CM zlxKAPm9Z&ZLa6+tdPOZO#58)HeN@@K7WuYEXX*gz+%(j5-S{9L38Cf}vOX%s+B;u| zM}wD=AO~y!sNGodh{TwZ{Mh2VE+_5;nFBEsZ%ighbvy_(hUgZNuULMn^=~p>bLZef zy+*zgC@z_ez=DNl{?=rE{EA%hvS?Ld*02Q3^p}OR#$HoscdKu0^X@!tA(ZXjhAFjP}tA zgFNM2Bq_zwWh<7aIh>K`8)6t^&z8|LFjU!=Ny_84p8vQ8#cC(Qr|oXjB=VmYtjo6k z%}G9hMXN{t)>dmEIMY+&R=YgZ!8ViHnS)@-Vsc@JnQku5i}lYt0)M}+dacC!!M%gY z&_z|b&gi{5L43PTd{Xgyo*ITXzIWS}@g!1JbX=9d!Wms9M9!T3t%H9=j3Y;>g-@`A zkvVgWg0<6K)QaNY)GMi+&`9`Z))mwj^PWZkJ@gwoeT~kLBDkG%H!lAB#e!`L`64qO zH|~r#?fz7vcaQl!$~WkfmaFi(Yo_4oO;|wkKT9elL0Wy%^N^saX-T zDPkTKhg|rV)?PTp3`*FBN5fUIU+-f8`R7$>DK5wqoz(({u_+k-^tKYkV7F0EXdG$r z#hq-Qjvm(w+|w~7(^t!(%@p=QAqL->>!8r`UM=vSaIa+%92}^!>yZx8x1CQ&(7IHx z|3}S9=>rEL2QabymzLA?zY;YX6)&I+|6mq6ge_qIU>5cryk6r(A3un5fr%25CLLzv z9j6u@<)n(FqQ2fLIp1DN1#!`e($P;*_sLAmX9B-g0$`_l zTxY!NqNnHU>#@a--^eGyySUdLrm_b+4Ukc9`CWJKxlD9Oz4yCF`mJQM?cgLB|5ZmF zmYhs9UNY(`o0+63a7czmt5PfBoSFWaLaQgaMlCt(q^XH=Cw>tyzVt=yW$_uyFxn<_gfh;g?Vgvv zj2=}mhHZTNLLSYlt(qV)q|P+2QTeI2!eokVqKS9$Rp_mNHcM*dh}%E2=O~)eEuaHC zR5N#7ySD3ngQX4db6-)fGN^_V*OrO~w73S>_Re+~Ig^=$vFt3XZzybQeBT*t^IC7F zr#jHY+XCt+o5I#em*_`V?7;{{?o%?pCmh0eD1sfd0R{3*aa|0huF^e>>+L78cNtQd z_+Gjg1x&cjMUkK@8p3VRfSp=&t-Uvm7&_|#U-0XgDnArUX3(@n1gV=-_FjKg0Ix9qTk5C^Yh{40#$=c1W-| zXSu<0-&_oUh(SFMXHfAbsljbJ3CDwT^ihYiJsL2NiI#mq4@YmQ5E(N}GX z>ZV4O(MV%Z-7v~q*fRap1)YC=(2W*?Mg^~>8q)+|zD%@96f%K5Q6g|5ZDm_{HGNS! zEiRy?nPWH32KdPn@mMc7r=0d~W^SDW*gjO}plXPnu=H0Qsz+q*XFUkfqJC~;RX^Y@ z`Q^~?7ge2awtZ?g+7V6iC2XJIqqy%hW3-ldZxhki5PnaQQb)2S_~OQ6^Y{xNY2>}< zL1X~>3ig-+3i9FoGt{_^(s2+R;5nT`73c#dlfGKBZFqfTV3PXb=g*6I8)yL3*R?n% zTvW&>O4YUwTZ9c!IHQFm8Qeq85?+w71{&}IZs$v;h}cu-pqaY%%xvW|N~F+o+jC9} zQFds}n;;q2ZTbS7sN2@hpO|LSF^Z8dcm04f-~;JaKXf?xixPu(FsEH2peY*(^w5`s z&EyW%jfUJYhK+F$05w1KY_RYXGhRJYo(LnLpX>oET)FwLfBn2B3RdYUWO*u04?MBP zh<9C_7Ig_TNbO>|ov9OX$Ake?nlj{yL#yAC5a&Y?_%O5(9_BYR*_Paetsp`0ZwA2N zTOK-9KBbV%W_2#N9qv#ps1F*bOH4GR8AVRey{n_M&)5f4S%{i#m$ldj*frufE-ahB zU^vGuvXjadf_Ue%80m!`Q-?QV9%LPk(#-FhHL8x)B>XcQwHd#`)>}sxxSWgY@KaFC z=v%3sc#Pp7OCVb%;(%tQu95%- zgeSP+t0%a>Yt?trOlW~S$*`xVCM)5U8B(us!mZdGwQAptnCWRyX4bCF_6Qz+rNl!j zw3Ax>Y9gVpm~P-R2m#|_Od(&AgtcZQt_Q(Xe0#8LHus`m?pGYal2K zYiYf7(S?*|a6skaJsZR%CBg7k`iZip2QV{4q%!g>Yjw4}cOrO6C>|hqF<8ykrBHku zpg;x0Vo_m`APrv?%^gQNgsMxXu;=B-r57KVq1rc>N-SO|XJI#q7V!X}GdIubf7{=P zr-2;UVnjPc1z0!2r9+63=WA(Va(!>_mK6i>46TK;7MO>~Q&i4F87is;k7)$t$li%M%n zD^q1&&;i0o>!QgdmT&;W_$JG8jz!A}BV!)yilyYGrR^e7(l3_$uK6Y1Bi>`t@w~12 zK@9`3%HCWg6=t>!7EKXU$_*<~;?_Q%kr;O;Mvrhz1SJQJu{si`pEgnaWN`L>vXe&0 zb-l4;%W6mjgLu6G#nK2HMyjH>#K6xVXWDO-Oml`Qnrsv*jimy=o4al(1a z>HZW%Pqj7QqO;_3;%2MEq>vRDl^2xm7jXLEGt;DXaq-bA^b~!!d7E#S9F6Q#yWasM zw@)iSx>NiBq#l3)Gkr9)eNC2DbeB@Ws3fxOBT2@}fF)|O^+5|p9aHWP=Zag-;Ds{o z!6@^!ig9)|vr+4CcWYUk8H?8M%s}_>Tiw!t;)W%9xaT?c;u(ye*DqLkwdZmr&QUJ2 zFz-mmwcxx1I*}Bq9ydL5Q=NWNgQB#RTojr&XAdk37#2Y56eC+_m8yvcGw~2c6ZANq ztVnJd)H>}49^aA@zcvoCHzMY1-T|zWeldGeF=hWLAz2{tRd7UB%m1)-j@_9>TN;gx zO2xKq+qP}ne8Y-u+qRulY}>Y-baj8acZ~iGXOFYTTKjqC%#2uuqxqUQbo8i;YGv*4 zS{h|sTDL%Jt<1Bw6VG>S$RW3?R0)d1U9+$~i_z%R#BJ{FO163MeWl$a zS2Lf>VG)B1={K)jNi}HYsw$%8@f)j%E&w3u1O*OeNN(0Uv*3Dy%-(cf0k;9i)M%{o zS)h-q#5JP9x-Iiol4^e-lsXPD+EC_f-D27}-kRJzU_F7$9) zpSpPkT2^)@+3A4(RfJW%3B4iSw^f($qtg}Z@CO!8up#ex%bF`o7m{18IlfY)Dkmqe z;QO8q5A-Dc`eS3WlT*&lBaf~-T?mjebH;PzFH3m8b+OY@X^IpQZDsMYS$FoRg_qAZ zr;$7d5FG1@%eZC6^R#swlUHg`7Z@+m4vYq4t*c22;=(+VBy z>y?lMw2+LMid9}|nlI@q9SKaw%*g$M|3jCyjjqucurZm6qJ)`pnZ&EQ*?^bKN&kS- z6jayx(|!Pk_PB*hUN=3iau2w2T)_)fKG3p;JCmINxpGMY|l{wx-n$K(v0UMBeDr@hJcqB8Cl#jG-kbE#* zw0*-39@Xa!g zSeqxfJ9kvZW;;~joR=Z-BH6zVsVX)<&et$i+#T3Gbw9_qVhxO0gSypp1-AvXYnB&K zQ7kXcJ>M!}(>IV`Rt^X?rN_p30vrogfSdPyO1NN5p z=WQzGzZhNC3r5jg%6_EFvYeph7@6W+dLafJl{SL$9kQ*0E(2bm$5ng!HtSdx0xCVv ztG-~(qpyF~VY?c*Tb)v0)?5t+xTM<3Styy4X1#{rJ2I}+Q>Q7MxbJH)7@$7<+<_WU zVm%SR9eTqiE>Xa=EQ42^Gx2$0;{!AkN&O8O?lCdgutx_)D-%cvG+T5Fme&KO_^qki-LbuLPtDDP81k6iQj zs;|y;qtazJ+jUffwylL&R>iv|;}vG? zDZIHbiYdU2;hz(&Lf_wdxGo!8W#dsRK@lemj?)V>k2ufCw=xRfWu-YWV{L(>5;rc#{6BwzysIy|XK&iv7herr7^ ziOIyO$9g!QUtAa&ds>mda2NRb5vnDb4v>gX*h>JVpaB$2vS|sV@_a9CF(k z{TRXuN&FzHgOO0x)w3IR7Z0^WEUTj@WDm^l?a|fA{}Ow8wt=pVhOlmWf9x*57R+67 zK$*Mw8{_p6Vc^R@5m9Uwy#okn{-F$M4wla{f_huzQcD%-2Y5X|cD8JH%=#BAbz!5q z%BBE#Hag!DP^;dDCwj|AOW3Tv=cwqjMmrIsQOKNn#e&mpK3Kp{VGiwu>H5$5Is1Sz z1Naut*Ux13g5L=D8ARGLP%3&mimt$wFSNYU#%aR0+v+%8JY2jG&9maH(@qnH4A=<25t@V2s;90WtRA5fUge`VX~aq}DV z+g?$E0`i!eUTfUZk4Wc!2s?L<+ubs}0449cY}Nte_GNIVEJmgCw7&QZbk1LI5Ns}a zKokcCo+0g~DXOlHp76X~$??Os!4r>qzHN0v%wP{O&m|naTilVq@Ll+xAx*zOcc4`x zh&<&#K!REZT#G#n9A!|Sc25(6H{sJi)_3Ipfz-D38RxZN(&uGz>A%pw$({1^9`n%o zl6r$x6eQntm#Y9ygH6_G3UFuHSyCWBuMH zulQUjwUA>xq&-^rDxJR-e*0MDXU~*<>wW0?+~EeiKOjc_1^97&eSe*`0-8#@jtu^d zR$n8D+XeIF$kHb+UO)G#kxKm(y5E$}^gH-RwLb1GKHD|x`sa-o`a3^okD)I+M!uQC z0evyQ+8mlIeAYXx-ki_9*cek%Je0>z?$vWQ&pr_FK5Z(`64kWTG+VDyYUi@2k4U0R zm#*vdHL9mn4N(eZ0@Op9NZ}ahR~qF2-ynTw=r~MtNo=_vaqIOt#V@z$k58Aw2pe=0 zx$H*|^?`)JwA_QpDLu9hnU?|RCgg3m>OOs@9c(2ne_(0;jkBfGh5A4Y7LJ;jSG9zx zHywDcnAbAC=T<+raDRXfNa4IXx)5)>n}HjXN`z4Oz<<}#>~49)G%|N| zq+P67ez_Jp^6{B}sGZ++MHH$5JUK1<35KFWH?w5nXSu8onAkhID%A!ld4!o)ne#C} z*Ynr90pu(}Io}8N;I_LnJYb zXnyXizg~>4?QEAZE|7@;*Nlo0FzRl2gf<0{wV`Tw<~WOA;)xg~@OtFgl$++iqB4F} z?!}%D)CQQG(-op)h4*;%=l$ylI4wqq;L^y7H=y|;iLT5kf_~IfU2>2;{U{MU`i=kr z&EPM_$3dx)JB7Y^Mk90~r^R3krwCt`Jd^s_;_b5p6m2jiBngrLn8j2K*`3!x0~NVb zU|(0Rf8EC3)WOeR&mU=Ljw;Qx3k2HhQ6cJLA#?(4OJIv#+8~X`Rk*3nG?bm zS(z1z4cG>Iq2n|m0!E>yj%;Q4-JYixW3`J)TV>-Z0aXTBu*=VyGqUgDI?9nXh z&yuEvIBxaLp}I}0-5SmsLemr&f@VrO0LxwtClw`6uoA*CUe^qDS>&|Dv4UVQ;|R#a zu|8pJ3fiR0*EJSZ@elMsno>|7*l5r-tTIe3F zN;;hH3Z^>an|s?chBywZ#XO+!F+C$3SJtEEL8u~Fbj#Upc_bTkXwI1HR;?5m?GwEq z%3sa(ClPwPWCg^HQwh%CcN$PawajArY&QKp=WSVWMcCahza#2?p zn}g2{b-%X&$THa|6Q(Y@!cX0c`2vr{m4E*jikh2%zT&bimsBnfR&o4>BhGK#b`yw8 z?@m|gfh}x|!9|EM%`ipswd*|X3TV?=O^QTLHI5nTdedT2_$6W6yKi*ZEr9QSGtfGB zQn-=&;FjHD-$`)D=qkdD2|~Hx)wg%p`s-dj^T6Z~AOUO4FmgbB%z-B=NEFntT_LEV z5Ib%ssanFQ8(BS+&kqD^i(QLFf|t=gLzI(^VqQ5_BD={A5|^wO&F>$9OE-f<=I)fH zfcUrFn;oM6$q9%w#`~Ju)?Iw;a`s+GKB!*Y zr>UUzYBf4dfgSExieJnq_3;zxnOviK>$vFvu=IMG)E>3YH%7Et4oPED8_$G%b$}M# z1Rrp{iV21 ztyK&xz9%tRaS)F#t(7oUSG4;tCK+gUlenk(v!BZwUn*tDa|ldPGxGzmV$|PbDl>zA z925y^o3DN>vm_KFF)jdbTwq~EL%3H^x?Y0Qs**LqXh zLRtY|rx{O&5NO&GkoKuEZtTwBBf`(#wO@ATvv$kz$BkF(oBj;usY;t(6gl=nip}S; zxxh$V*Z9)PsSs4X-HwJmHGuaE@Qk0!_kHcRa3sR_1uIwb{RO6POJNX_G+kjb^y4>%fJm zJs$hG?G?eEstT4mK6cI~G&@p1mcX&A8MO}ofo*qhlC$kc5xRDcB-84V8>wzuj%|ns z+6P~^!w7z_lnWG&@38y@a5Q<#?AR73Q?NJU_>AfT2wjS z_nrH5=WNH%v47?Q@SMhuU@4zt>XivGwT3;+5+-ssu$zkh(IE{7qy){@2SsCf5+Kj` zTv}4h!J_xC!QRZ{0Ba3d6JDo~$undSb7{0FFsdebMN+Jxz~&k+BPipW$W*r9iLS22Ca%pF*(M#7-T zTb(G3CEPH{N=ZXqmVA=3F#c4@05pG}#Ys2cs%l!d68wgo`p4E7U4H~KJs#oRVTD6N zn{34yP0#xJ`elbn9M$%HN*y;e=_zX$1NsaFV?GA0AA9lva4WB?0Fj&e(kqf9Q$AF} z3PMSvV0hJCa=@y@$?!WM3jNbw)a}j#odwb@licJt>V5j~G3FeptOOPv$cl^MC1e>| z`HJlMSM2a9t*||k2fmE_VasGpke@VO9jCPp)>Oum^PB1Y!FydKooR&7Rrysv6qc4O z?YARAd?IHXpq`+9Uo&2WV!4iy=*uK~-0-0hidA6hyA@}+BzB%3;S3L&B{W80s>Z*@%&NsiMM-UJ7y@1H0sKqu?%Dr-&MLh^0F9Evot_ zz7yy8rgC^jhPV~SJ`(e$&xR2tCqkGo}pf&Es%G%LQd4o|vmL^iE#5Bzf zm9|8Hg!~$Dt=#CfaH1_`)&U7x6m;*c?u`Pf_B^%6^u~`vrN4`pL=lVETvvdIDcU`9 z5@2zajQIVq)}WDDyb}H5tZ!27oh}X6OYyaeJ$`s4PMJ6I8!Fy?d&zh;98SmB1Ow4q z=F9F3z@NI(4z;s`VN1&vCS}aGD)d_}3Yfa8fsX?tdrC`>&ghlX=5Ge!o;^Oj{&HhT zfmDZXNdQ!2cZmXFn(-3wrPOtD2r`-Lq}zy0D?_!2w$MGD9pf2={Mky~Yg|auRAmDC zsza5dY^%W_?PJyzbwtD1b$dbAeBl6E)JAe1V9_)~a}^Esm3KASLnlZy{YtbX0o}=H zl*>?7)s>rAQSA&{dlGC*X2d%4ZPIcOhj zVaua(a`?V4mV$pMZHlT@;-4Y>chQn*nKLgoc8*1RLW?(eB&q2yNaP7n$~U;!OKS*p zz_dnTr6Pehe{DT6tiZhr4|$aENJYe6mb6-k*&LKlTLr|>BcU_Lr}?XFcS3z z@M+zEse!LmEwsE^a3N4H1CO_cu;|`fVSqjQmASod~K-Wa& zUTd9yifzf#=sIUPZ@N|*B4hp{OHkIzcc$x(}{Zl1B-I_9yd zjGfl1x*J_x@<@7m3e$VqHDig;ZNG?QCf?v=Fw;980v+FYNu=&~U;rH!0R@M(#vq*y zQBUq6y?rGq!x(S52s7nzNCpR0Vfzi~c{HMG` zi0QdG?Qq34x_k5@uTIe`9RN^erzm;*kvC@qBexz1 zc7qZ2OvJROw9X6Ypqp0iKd#V0>|sa=JDSD3L=5a>EK6^rqT7R~y0jbQag%bFZckKm zMUgcak`L5oB4z1U#i>LTq=I`15g=KoAkh0E!aX3WPYls6mHS(7p~Rzgi43ANRjSYv|kkhr>vPh>pvf zQFu=cPb%u?zAPqFTLYLyh!4}2(2#6cZ(BG;_#Q&ZbKYuM1)RN{wpze=#W_)oLNH~K zy_U2%_1m?T&H#cdAA>=~6J_@%jzz0pmtaK>)`O=Z=zJR;z`Dv;xcPo}CXf?i+jvE9 zm^j8b*-{c#N`)0(@;zme`mp2LlN088AYT+woUm|xpctwt6i5bc+3A>XA@9nzQ(Z=* z5d9{);kfLxL(s~smgNyZMrIiGuhF@4-`3~vKL9^`UP|#pe!Z<@Yzy*`FHfL~yi63M zUt*HImY~oFDAO4j)8NO+=AW$-XN6rOo%B+XfoD5L`4k%iEagFEJs5(Kw5gefG6NO{v9T_ag_$d)l-dNY>Z?as>4RYij#4Qbj zmavz#F!~G}*4e zk>(|rb0z8n3vbl(iSf$AfQHkg*xNsDCR&a6sbI}HdRHiing5G=X z4Y<)12M0tq->zVBe_bp{?ZwhB@bk&@1HP2y9b@F$&mIOz%;^t{cvKJ;PCoQ&Lm1qJ zG{AQtUo8kXujC@EshktWZ*#V9YLXBg#4mKa3-i`;O$&o z1R987o!MyNa5(Ta;0t^90SHClH$hv-jgDgK8}*Zm^SFO(3-SW}LTzvV%GUsjRjSk(XNF|ErdTYm3WOb6Rj$CC z?02fBcPh>XrJ^CIWU!OQ0dft^x&~9$RFj@ds#Z5{JHfMU?vYI_i!0Kl zsU!z+&~nfjP;;Zz^K0W?aI9# zl<6pEVP%t}J87ZtjGfzfT_}HE>IwI1&L&l=!Np>s%+vmpMnJ|TiQDOsT)z3=d`1(4 z;J7(GXJtwgx3$HPVhSSK7cHoE*`We?aS-KKO5zG%c#YL{m5efBBA{MJoSF)RNCbic zLs)C%TmjsXW}*1lNnkZ8z}9#U(>2Qub30%#!$Kr*Pf?jeNZYY-j*U7l+?3LQox&KV zt%#y$-|-9vvOuBRIJ-bf17xa=Q$lNZ{m3q%z^FdG2){0o82G_#Pe%Rn6}t3&=d+Lm za`2^q{H*V7K5LbYDwk=$5Iz?W>so+3FpQ}Ri=QI68N<^bf}a; z#5r=Mq!r|inMY}a^?XBxq(pYaujf^TS(q{-6{I!hq`1d>LM6(VHQ@ucXP4}*>!P5|H&$UUu zuf1bPbNBqUyf8cJgHLA4ay3Mo9xU2tLN>4LQfNNW8Zb82*BL$Vu^mrKnfJ{2;ql7( z-eiym*bVkHcOO>_4vC{i5d}TPEHl(%)jKLucPK&h&|`rEtiLtEE@;-y8+W z1?WU-ct2C(JW_=Wuv5zBm$t1XsfFllL`O|!4GS-&r^cOV^4EG2Hpn;^w7W_!?!~@ZX~*y&IxQK(y5q;yFzbPR z<7_t+w+N8`#E!!qD95t4I*%D$r^SEi=tHiErT4TJg0p4Z{%v!(-ObJ&@_#k&M=B)if$% z1+!A~A!9mPswAeJS4236QrL`@r=i(!YGOp|7D{UCcK0o3kbl(}?R^$2CbH9uB)3N` zS{pOIhc%4gaji+u&@tzH=~+4K1e`T zb(;$T;xycM*PAYqWV2(LiDEb6Jo6ecE;i~FR67{1NYg3mRbN0X)l51P2^69X3M9jw z)Rdm7Y&?_^r!3_=nIYDh?L=F7f);!5$d=H~qQsV+v07(9NrC5l(c+j0_49X;ks;Sl z7gb|rv9Sns+&7ZCkW*D{Yd47OZ2cvJKqB7&osS#WEbmnyjrJo%rqxR;go=uCb6x>? z$<8k_38N7BmUF66#jJY$^$ zp>FQ+Vh4O;rYlNC1kf>mmC~P!8if+4$MdMwPr??i+R8(@Q|Lea@;b+pS^^iVWYdKK z;`jcILtm!*#0%q^#iETt9BIFE>u*P#t(+nb`$eiOg>8df;2J({Xc3FxZ6m<+7=vRP zk-Ts~g$x=Ha+-W1WF#@t{g_g4rpN33m)ArPtLJ_B3P!)l+Oc5@NzF#~XR(s;WciX3 zWarDS-z95cmrSFMk|dE|;>ffT6N}maz)fR0Z3#)&*1rlgwXId9#Zqvu*MqX(nXt2K z%vsw>AVv5HC@B-nd$U1HY9N`?(iE&+^`G!TXsz#J48HkP3n`vLiCX_U9WZu^b}5 z2PaFQ+G}q!r^sX`9rGRf*A^XJ!q*m&;+7fW`IfUH(;j9N$_@Yg3Gvw4c!PrOP1aAV znndEdNw{_0rhTiscLP4GM%&yj34b$ehe30BeMOPfMz=Rr(GuE?hb^=MEa#n+67>Dv zvfXc`!+CECgQj^F6v{<|aEh+eJ?$)x?d+6zE!M8S9@aSrG0x*LL~4@Cd)t)FKSB*NSi11z$ef}6T(z* znx>i+weNJFCaSd(}WB472&+t0>(+Ct4-sy+NQGpiRBj9J{)zO+Ac` zVXnVSdZxJu*fwPVqDhn5OiSuS!V>fAU|+8i2z&D%$;t5F zpV`>U>Lz_(YVhN4x80?g{7|>jDubuik?4d*TZA{b;BF6cVzxSMOzhJU-vNp}_N4VN zBn!cxE62K#tubl`K%I4V5C3p424rPnjx5DS6Yb^Tc*IvAwX5tc3YXY*T%Ag@Xr$89 zeV#@8%&atROrS!s(x@~n8Yzt`@C%ZEDZcW3S{M&uA08n`^i5w1_35I;FF6%Rm-d8V zDTDQl$?Y?|{_#F8kT^7fiP?69xZ05UUl)?xQf614yNkYm03@V~aE|Uj7`gSp$!c#5 zIm%bswvIb*u9yl~=8+%mU&j5xqRIDk#vnQ%L#_6%{E#5kEI+fsjOs|y!Nzx~wEn^Y!t`svmmB2hJEpRy%ywPIQF5&r_4PZ&By1=zH z{XI~~7nA46fX$L1LA-;}k^9XSQlO$PJAZ=?-*fFdHG7jdcVO-aG=@JC<{81Lmm$aH zUJ`4#zVnoo8^0htJ2rYL;O03-opdk^;*JfaF%B92>cUQ%PrZM-1zCY#Sz|0eUOYmT zPO1sVNi7Ic!TNOMLHIuR)etevWv5*i13@T<&hHq{0LCVz3TYAqi0mN|P5JS{J$_wf zOqv4RZSs;41VHDbct^jSKwjj%O~S$1IfOB7K*w#GMcn7PDaw}eNIPQXr3rL0YA}w$ zf1A9Ri}5qXu?DVL);$50v5c}WO8hOGxp$bfy{gQc0RvvIr;8vb)Hd}a4?f+Wp*TNW z*TI#I1x(@#rrKM`3u;F+)T=_%|CZdY1ICuTNn2@e>x#^qR zg7JbQP31{uRk(F1)O7kd;f%R=5E9#ZF-|8#KFtN^h?bf{jMLfe4Py-K&cqI(`^w5m%#^LOut$h8+NK0Ko zzY_1~OD0`}jCn`tZ_AFTyqS?UL%TR5KeOS*>F-!Nr4vATRbls%5>OL@GkUv|Fe3fu z1?0ZK)Z_bF2seI$&I_5!qCXj8M*`bfPevvn9gzpxJ&^f+9)2@h=w@X$!8iV)rgw@G z-#IwF7-W&}8Mzx$zPwqmF^XHj5AoIT)xIgQPwaPx_t1{)k^7Ou2u@7C`y?Hd-r>XS z(`|D*+GRvYa@`I%K?-wu`(-E9SkX^sF1_w{^H&817{jp$am8(FP`5S}q{_yu7R^cE z(pyXV+EPY_Wo+Qgy~8}!C99%0v-b<1l`r%E>v}M<{+|($jf3Go$GIi!2|MEUyPVon ze1=q$yE!m0To4n`!AO4~UlX%gH=T+t$T+BZv;Zx%E+w`_S8 zGd6Q}j3mvaSMNKr)W&W;t$6sAb)i?e0DU;WFGRXwNbGO*IGP^ro4YM;eYMG;wtYZt z?1p{sV2TP1n@#LYrlMlzlvq}meXRTsm*rKD#TD&0|1G#y=)~HoGA3+E)EskL4!+Ii z9dsXO_Pq#BSd%E#!d^t7!}rSpe@ic~A6Q!)2nZZ<-yR%SkYMSa`;@~F?0{h1Y$7$F zC*))b3h*!q*S;pBvh1dD9>>|5eX&c z(!9ciM|Z@1Ne!abfKirq$LhMubvFo6Zbk{aOqYeSHdRERCa1BvcW>NL%E!Fyz_-?rFD5c^QPs zy01Nl(T`!ZNNZnl_SD5x!T9!8nOlI4E9zM&{So(W zb9Oa)i*M91m~K@vA+S6(9m#33oaH{_Z@K)iC_I)q7zZe;9o#*k}Hsu+zz=zljl3Lt@3H6EfW!p|wD1(AuOKZc+R zCsmmBuOcE*{WUB^4nzRjW@}EtsD9woh9J?Ouq&+b4uo8@ckPq0IpSajiFP&E8ibSF z<5WhmLu{PNCZ1Rm%eeW)?q1CK9i$5yIWP4e`S_>tl3|jpTKJFFIKMgCaZv;A*X{IU zuRK2vQn!eyDPr|7YN+Lm!P!x_uXQxwWDKFm$nOId@zwhS6eIvTW|PYuF=)_7_KNRd zN0gn6av@4c z&ZjV29;;S7X)T96y;3V^Vckf8UNVP4psf+nLo%HX#PH2SP7@Yjh~A5TG^&swHlgG3 z`s)7nYe&V#r&@r>2|a{6TC}nY&Q|{}Gzy@sW8>0pS;F>;D=*bfZXUYqlK3~=z>xE) zL2Wj`W+O^m{Nxcryf6sqLJ~u`F6iE6A`UIgBHbw*+!gEN9c$V~$&=*Z^8M%-DEyJ} zd?0piireKTqE@hnnUl9iPh-d(c4SU`vr4@6IlOfYyA&`kUO1B)t}&<1>kZfkDq!p@ zc3erbi9^9jWHyqdnCwHKR_4xpe!LTLMB*R9CmgREJcZcNqYq^&NeoM%`ih`t%7aIptKC#OtLq)Zw?lB$eq1E~J2_HNazAZN737e&qhvk4O72O1iHfuc`{@n;J|o0 z*9kBk5_*wZ%_clh9}vefY!>C2o;H#pee;k;+gs9mZ?tm03DP;0Cb`Z1qtjo2Q+M&u z@YKnizFLlhdW}0q0R!xt0x9q!e zv4Zh0s>WP&Izec^|SaXRZx#Y?{Sg)WYqXOIvBSc zCeJG!cYP-+WAU(G?K40#Gq#d*J1Lptm&+0f;#8Ziwi6o9exWJf=#3Y4tQ!&%dvug) z1U<(AP*c^OH1xN2WfqG%X0{~b&;W;#@QI+65M*|}NdnkgIAad;YbiS4wUk57ZvGVj z%)SwK&_~xj`#!wcbkydeLS3s%V~%&tZpwnqNt0HErc>r9&5TP`xp`XSD8a84Y%Z7( zuLT`ze%o}Dblz|1W~>|FlTQgvgUtZ0j5f1*DV+{$89O!S1})|maB%RNQUFJJOq_P! z@5-gi#?vLn$|y^Lqu*K;OBzb#ovAD~pIg{|Yv>ao4IuL$1zQ|P=#ecaetrJf% z+if0mucYv}E0Sl2dv1AB)KV03$U*btnTIjUd)L3cw~Hkw&JrA3*0?WY zWnR#ks0md*JflKqK^zU(E(6xd6%2e(r4c>@6<`bEG&`^CGR1V|&uiPDw-8peSC@U5 z8&9M|hT>FEwrX~$;QgDMujEc)YjB(0M42f>+MFVZqpmz9PY(s15yo|%!MWp3^I;_3 z#0<1gb(@qA3VB_pu7dN&(qq8sAJL|KP+Iqrn-?tIsUoPtWcL>u1}b%DEnDIRi_pWRqO)Y;D#&_j~fgA>ySvTPF;Lm`tj-`XH14 zdig(LULe*YE@p*|H9#jEtYn~JL98ive7sj06IVrZiF^;Ym zq(1w@+14KBT(^Td;Dgz?@|RBy=+<8~hquvx*TsmAC_CtgDnPat{z=6TpH=p{5PhE9OEQ=dT72RuHMiEpT5lj7 zcs?2MJ+1FcxFr;q^_$jwiUdyS1k5 zNdA}aPk9brqV_`t4F7+8f7`@%aL%TQqjnR-&eSeH3B@7`*XEJL7W5!IcXdgW;hor| zlpXX=j92s2%ap9~j%3pxc)U-LT>Gz^^T?nd;iy{xoa5UFJvrjfYs=@;#r?vJAEmV< z9e`F}AHCDe7(LbSw#CQyZDkna#YV>EZ}1>SFR!L^{?6DyBQ76akAALA8;14e0(vTQ z6cjk1Kk5rw5931#AM?gjV8Bv5bDQt>c}M@J=kw}9+-5ixEc5QbOvvlL4R}Wl?$6tp z`YM_O#@*EPj{R#pQu6~^df$(f)urqV7r-42FU_d4sMjAs*Jko+<|TzY-ER4CfrB4) zHpX&ZZ0&foGf0*bd!vWeF9#-auZ+JpxB?}sZZA{4NIVzg&{D8Krj3vD6&r@m?vt8w zoF}!neko6MiP40&AW6e+YFCi4(-yw}3*bb@DFZgJYyuZG@|VNz@74MMF%(tTIUw5I zuvixs=Hx)fk$G^Sjb84)#877CUlE!Q@wQ(F-&!y6*1~ZKXmPVNjQ0?Al#k#!z&}Pr zRpxtaFJ+c!F6R{2ek$NJ8;YFjK>V&GNL6)*EJCd;*g%6Yc`Mg1W;FV4O#QErPAi80 zl^KjCB=YNr)T3_RjpIqP*3hFb51>bW1x>aVhh(FokV7ZXqIIl-5fIU#q@yf{SY}P& z53_q(1E&=3tD^XVfUm`Ky?01}v{a`UY^SG!}9h@ZmhORI}3kVf9fTlf@ z|I@0~nQv05u#h4#`!LAg^VK&>rR+~&IHkSJRZl2TGjj>-CozK<7Yi&j3I7HCl;k{% zd}EkXF^=?TvjfR{0U;9zu8Itw!p6uO#QWJ(m2czrRvSi^%1YY#*%GXwb3g=fisM%6 zxGAtD@xeY=O)ZtYF>=6nNumD%yJaDYXY1#hPzD|@E)W0@m6;;=5h@85i>7a>$HB@( z4PK=Z1^q2yy~)96%wb@o=U!%f@l?L|M)|lUu0dH_U)1W*5<3On1h}k#jSEr*>~W|< z!b&B*gPe$Zk-*eV6vYK(w2_wrclmS0R;doWf3*&a)8)Ubfd>gfUgU zr=7DyNQwZJ3O%$P4ai6tG-LL-5>&T6U3$Ut&v>Lhjx%x0Qfze@b+g)i1*Z9x@7F}R z{62??2nr(v82!c2<2hyhkdm!dIkrY6t0&p`$$NAhTq7pA6oFTlE?^)LBuFM!3=I5vs zH3ZSg?+CDQnh&sA5GuN9e=`zDZYXqGPTc9tDnl2QR=zxP!6NuyCZ>=E*<0-g5gZGh;7tsWIV?mL!pQBjbbYMG;Sxe(UEO+z06L$qV8hxb z&x}M^5pHdWCWYOr*Kx^$DQCMH^XwGZu`foIlFTMdp@OzLm2kog)M?I6pQcR*dBtwS z)&vAD#_Q$l*A|~0t(?LQ+)_YOwpeU&+^>WS4!3|7R) z3QDL_)w;RLatc+rR+7DLD5*EhM@PzdTeu!eeyn|TL2PWyWD#-lsl@1g#C-algFyd_ zf)Wj;csY>}%(04-?Mefkp4vb7u`U^u=l~e9(YLL^6T2m2Z5T?qM{wsWI3GQeDnK9A zbkd_|D+?L7Kxsi8)I*sQ(Il%hBbP$D?0|}=vJ)5d5E4~XBrhjtC8JWt-FZ?7sV3(v zjl-=Y7YadVmDf9leg0C)2d~0O^Ega0D_~534vv`4NqP3wjIj+*Wme>lwk=y-{RU*$ zOOb3YS)aD^3nu26IsymaNP0ipR90FFpN6@$>^V-la)CE@=dW_8+ZQkLPxC~WYg!Gq zGqvtn9Fb7k(cvsA`k64BN=}(GZ1(RKZ#n9riQJu(DZKi!EC}(a9upUM-nnb1B&_hA z`Ow&u=XCHJoPvh*&sx^b%)5Hbi33tCw8HA%p&v>>TUopzJ8>#_I1|iv(Of)=LO8I^ z>3*#m6+;(~w;n`0$ni1bc&ecM_RcXi{s>#*fCXu1GR>6dA&DZZQ6T3xfF}mGg^xR- zU&MIGsSd~q@#O(K*+(fBG=4j~RD9=%u&5u{=whclxe4Bk=h^_8vc_fUQU>fdl99!a zn1`G#IRd#WZsorKkfSfPHqItZrF%U<;3rYvxK797w%TaeD?gby(&CZq;m@$iJWuLl2EoT3BO&DoaiMW=#w72bq} zo>v#i%nU^8DHpO>S^hvnE&*Cg(}7IX@b||Y&DQzvIh=LO`DR?qGN+l(#$t2K=c}Ao zEypFLJLL#?>|3D05#zEdBOTfoW*FMX==DQEC#!6Ic`n~hFFeOOU7cfHdci{jFKnriJb~ zzL7akQbvD@VmcXE!~s=lepkx9?)N<8-~}C?ga+Yq1ZgTaFS9I`=+{fni?b(RRp{qg z+RA6}I6Xbo?m+t({M$Wyb70JN>YpV&3+lg4zp`OHyEB@D#B*86T5MT4U+l*EE61W+ zwqq)x;0?AjoVhIdop2J@b8n2B$uv$xI~tBQO8~bWoJIRdE_(MfbXAH#hlWQjIhfj? z!li+6v$oaWj;VXu%$2~A>92#v=P5e;Vle%$tex1L1Ri1p(Ew$uY$f>_A<#9-ZCIe* z?<2qfl{3`;gZp7(Oa&$dB?GYjccSMXyP2f*DyOy#|FmHn(O?z|WC+5?xsH7>G(Bf~ zsvp7VByI_5Ef$Vit(OV>9aCCA8jhm-=2;pOAJcLV8zP{gK2{QI>R2dcZg z+PD;uFKD_&=^Mz)k`vd-vz1}Th|?p+9%D}Q;lrnC9X~4nImOe;M%4B4sQ&zWn@HDy zxEvc~pqsI#YZ}q@p^CNbC)7?t!Tm_B{(@oo-*?Xf`q=rS%ob8IJhM#45%rhp`k`zx zjcBuUBGd7{8u|LKy1q9_OG7_e4U^%^ttvDigP7IOX#n|~Z=NrW$@+iTI)^UJ!Yx~e zZ8HNIwr$(CZTkz`$gpkOwrxAZcIBztxTjHL?4R&9_L_KR2-Dx3IPVJvE_9Gs|M`xC z3zuV2*ghc&e>t`{-qN~km$mFCkl^wC{BVmLlBPWkE|0`hgBc8O(cwWUfQI;zdA6p2 zLdF9qBHbLi+hJ*%0SML^e#_ANj)gv3-yY1tkO^4LtwnLvuQsmrZLC!aV*sC;43daC z1`qFo20y#WBP|gGhq_oDWj;**OA4lyu4d#=;ACLz#}raPC`RhV6K5XOu!CGkC!Kjz zF2SG35j4i8NGI5Oo`G)hsQ^jtv%j2kUJZT zH)+xpY_*Qz)^&9{c#kzDQ6XatjhTvhy5u24UEF#qw6q9F~B0htr|6>#b_CX zBG2gjWJI5@JKwzCitxt~5{A1f<+&!oasIX?i90t^T>< z?3N^2D!pf-Y(vhX3!r3D-X+nzOU?%)k}xvP+I~K7fv7b#jLFibEN0uw&podUlzrAkp{v_BE)DCi8Y80wkxS4n8p=NgZcXITz!6PUcRl4s z+V+tgpMP=YB*305q(vjTxpTG$QNFOp*>`U|U_6ph{u9l{y)KfgE@e~z1{+y#-pe{L z#DPk-M(U89hl|$1DV?Zr5YE`SFa;+#i1dPxpsXxQLB;iYzTBcm z;Gwx@kZ#wMzRt;Fp#yL#-aY%>Wt~-sP!#{A4$)5!4;V=7t%5M7@_8{gf`C-I{LLtA zv866+=_&;$PB~3!NOPG)@aeMf!xV74rmjg;fZ`4brViJbr;SA|DES&>w$@0Y^>wF7;fanA{nU{e; z+RRzT7SO1$?0;%(cf#yUFd~o^e|XtI;Fa=)MKmUp^0=zqw|ww3pQ{bdkVjp7GJ-Vp znhq*8DZ4n8y){(DG-L-X4qB57ZUDoe!F%B?XPI5riIig5MKA!LPFN0_k(Vye<7V>; zH!ou;SkENSI+%wY+yjSQRKZQn^HOg{&rs253GmNs&ow8=9d2rQL|R?ekx(UXVotIw zZz3`jl6!HEMhlA;^q%Dzz!_fWCc;54L%1DXiGZOofI;Dmca0wLiG#u;^$qMy&1Sj4q)gPXwt>^?mL88+M{v@M#brp9YAP>d z96*zIWxYF96TMbXeW(hki9j&HPs+(S+9mz9#Vx{IG8Hw;E?q|Edmg1kD>7tuc1$`OXkZs{ol*nXJGMgzLDM>+75h6{ zCjIf9x8;sHvY|{Rb~&+At4!&G5U)@0tR)q?1v%enZ@6B)9KiQLxu(b<{j)bq)TES=-hqaXr#H~1FRw4PH`CITlhHTM?0Ij83x}BqjeDeodeYr2=P2Q zE|8>h6=agCzq)HFazPRkYlRvyqlrD5#Xy4ww2sg;*-qlan5nY_BP_V3)8LxJ2dy@1*4a(s#rMR>X%IVQ^~XD z(s_ytrMlQ4*JSxm0zi|e0D7sVt6Egs~O%G+h))n>HP8WSagxt}gu{ zy^Fc6dqcBiJS+T=mQqT6S~-(rj(j2UM&kShEg|_2B1R+r&wRW>z!c((5?+P@7eeNJ z*rEt3QaW3aGIVwB$)l;4w4Nm&$2F_9iKF0BA9IsN+w+6kUu(v?;tJ}wt6f~S)t^3$ zd1l{_*qtmYnRkl#9_y0#ooIK+imT154}!fz>=8duKrRMDOue_8c3%b4=(4*jIR3)b zzIbv^A;8iRFva=-;MIN@#`M+rAP587J8FmH`?KQEX`R?dxDBaJD;q#U3I9@gmHxZw8Lmn4oF0w?%%KL1@n) zdcnxSHFW$v`%q|Mtb^k))HE#z;}4k#vd36udd%#e{ZH#QAWPp7^bIh8Zg5N@(Y1as zFwbVhC1d6}<9WQ*^_PqB+hRb9$8}tWm=tNai2>BvsOaPi-_ZV}Tu?}k*+$EU$zi$U zl?lctW&$Z_Bhn}Hoj&6hWtY8wT&^3;pADa-vMXqL)oj3v8y#Eu+1Y)Xrs#tTovqE6 z@W@!)O&wRYr+xahpGd27oIU6+jq{&Pasz)I+VYISo*BY#!E`pQy@}>D8SR6RdBK$i zqXIl-kQWGDrg^30?ksO z;6_!U7fW8ih$z(sc19klWh(utTNKh)9PkrpH({S-JWYE)=u)B|Oe*e6`(RheycEH| ztXM1GKF69>o3=CJZ-$!{RomPbUiey&8V8U(Zcrq&9>g+j?&CO-4lDOS3w-qnzbK3T zj>F9&s3e5OBwhW=c&{l61;#AUj3e!Dz4$WP$i|uJg-vz3NN3J9 z%~hEcslFz_p4*lCWxcG#YwIpx>6$PkR zRg3)e;ILw|)zj>>JTw#(ZwftPTV-_t3v~{^7jZwzY7_t0*!3eBoEnH0B`W1mON$yA zv9@l>)|g6R)v*{G5Gi_stXF7YVUQK~D~5=oifgvV9_3MEz^9ID4`-Z}lt_FyWMgj= z{!-*4e7U^Dz}Df&FN}wY zxV5QDk=}0{ret$o{hcNMB3#_J?1*w{3sHr&Rqf^oW3M|Xtdii>>Cc?hFP3mV=r#X? z7#`mBxv4s#fYg$gTlvYIV>})y#HO_FGEPOl65w{s$nMWhD*>oyxRzkxk8RN_=6K*(5jSiVwU8lk2a z>Cz0o^mx%|e@leJrb}Yo;o$rQnE+!M8`Hj1l?c&xPqpuJuY4>iF){6&IoJ7Zqa+ZbjQ{CLe-1uOM*Ssl9v`_`bsD{n2dUTEg) zW>lb1N4TS7qh5sF*0QTCQ#Z19e4OTZc9u2>oF^XeJB&jDyE23Fv7x!~%9ZKwxP<_d!ThMRjUH@`WuKPJw-gN`yX58sxU_=RS{rX87r#oe5bmc z;@DMS{1Wf|c(`Sd!qSOSy)sQ7W%*eXQ`TUTNNo2iFEN?O)k?lCeGv~!EqPH(B(vHo z)lH6Sb5Acf4LsD^3=<1z4wzi10KPN+~Y`Qq95RFrpY9N z0++p%>89NAz0_l&asnXyo;s=17!mvPeAn_3roErS=%MIXG3=z3HPCt~X4_r6M}M*2 zg=%hMD(A_*o}?CR)A{zhAtG-K9-S8c!ek)M1k)W{zU#=)`exX&01U3(5-Q)e*6^rQ zVrD}J)bPGZzxiX&X`_>stNW}}PrSUC!CJr1#PV=cW?{JVkqQvmZ^bS$iW3E4=0;wX zfmc9R44Ps>RJv^Jf${WONQD=Ef%6aKI<3J&RyaWjvzNiXutjWksu|WO!A+{fYH?^s z!u`q~5KAhfLVFmuN>UmgCfOXTtf$YfU8y^=D^U*KaHH>oqeSO!#XoqDfVA>QUHqLi z@4Z?>4}U1z-wtrQMn`k8G!d>8;dS0oycT|0twTOb;fe!&>_U_mMfiOo?n%X_O+crM z!P0@jcRPhg{wye)iOIevk_K_IU?g3)1IKD%{+B9 zSHAp_z*#)^!&nn*4E$Vi^j`b-l$<((G7A^>;a1jx4Tz+hOikCWyeD>{TgpQ;!M9j2 zK%R=j^u+?fMIaY1i6{$;64%-~vv9Wx>f3DVsq_cP3e$S%|I$;E9BoMf%xoNN{~f6t z)*iPb;Y{qFRsUDu!*u5g5Ws^-voo+`9@8#FR5DS?exG~#2_v%2$3AzVOim9u52Ye? zrxVl8#h(7RNWKVmOgc~_adlwHJATrcN$au2iK_TCx%Gx`{gDo4Djvv zHfx`{`+WkSH>Dmgo^0Kvcbgp1)2Ru1nQXoJX~RC;iHVp=yJZ=o=_EBl*PO z$0H-*zKgJDIckuW^Qji6N*R4M9R%Q?6aHDSTd@HQqrqlX_MSMjtL3+ib)s{pq7IwL1q@>&m!Ee+If&7q&qhIJ{4@4`)hny0*Sl+* zq*MZ^z0!!A@#_+_AX(GDp4lsl4;y!#Bvjo<(jn~>)xjh0QOg6-cW$w%c&prw-_$c( z_B4A4%cX#=J#>Lnc2MDiLOoj7OImHzQ+D$;W!a+*0^$_q5K;UZ-flgXK#89z!z^t2 zS+|63Fj5ep!2=kin+<}V^cN+HhWtj0FA@PWG^tg_nnpa}rXkjvAF*g71I!yg3vzH% z83A^P+7)!Qu>WKQ3qw9;%a}zi<4Ic%@P>Kn(7^X34hUeNHz|lR1XcLwZMh4Dlw!w# zDxhSXqJRsWrbL7Zf}BV%QM{S+1`&CwP#x@5Om`bIi@AVDZJszLMFXtZ{Az&>ElEI! z$fN+IoX8c3R3LJ|2P7B3iT9`s2D<@vOch`dez^o?+xKU*d&`Ut!HiP-jv@obsIcBv z(XGc~V60f{OWSp9~#)J!G64g#N6&E z6qt69togh!272N*D4h2KL`5N1e;$TO`h-sewSO2Z-dAZFg?(IvErx^YTl;hcgxQGL zT{y4xTn5O?**;rDCR5$>Lqi!XAI?kQYixV#?Ll-SoP6=P)z_A*l@-rXfHC0Og~IlM zPi{lZSPgZ4uGklmuIBn23oi{S8cZX%S3-0Zhb z0axFkx;6pnB;oztH>pONO`3XL9Po7fjPc`J_ty!!*@*j<%CHiV~F-Rciy18Pbpx2FEiNb1rKTO3plCZ$Lj2G9~=PW%`Yf+gFWSt z6Vxb|rAIhC08dG2S{^m~5~Ll;4sNq7L5eN{ed_H56j;bh>4N6BiL9`1T=rXn4)m}`2=2&*dQQ~_~A`6To zRZiZ;oOLw}=3Sv~@igopK{YP({6Z#j$~|FowaCkv?4?P8TocfAHxE*4H2=b+wU=^V zD+-f4iLqUmAcdCxT+sgEMCKAlNfkIXjxj1L z`5Cvf;y@(I3k6Rd$P=vd8-Zqy7JYz@qiiD*{)V;ptQN^% z9tdz>_u?*N!42TsAvGp$krrj@5qkLvNyv)9JE$f?x1hMzjr}W!7O-37yU|LAZ zq2N>f>LY-SVR;`n-?k@K#h+mqnr1Z}=_81Uj_&SktpYGJY{fIzsXZZ7s12?>{GefW zLu+e}3@7`U>fpUt&H!Z}OzJn=_EtA>vC351$2u8jhDEekj>I(Aj#k z$EgPFf^ycjEtH#p%@A6bUVn`L^R-7X2fEK0%^Hm| z&!b|+BmyMgB!Ehb5$`+FxxnwZwlM7=N0PQjyEb2*G~^mBDEv7Qu$B9yULgy^k#;tq zy16vmFE?cM%#(TIQ%D(~7ZwZLS^w3|tjsR1uVaOTCPP3?u7-@cwC)9r;<_p!*1n|i zhelq(oj{0nTn&qyGEUV3gN@=r8zv+%3|W^h-w;4|1R2OyV+_S;mJ2Q11d~*+90`f2 zCyC%lmITfHWGPm8ub`r?{8iVZ(t0I(bI?l>@Du>i&aTDZtFqt!tgzx%`)@QFvuK+P zI1e@}X~4?K&sxP+w=uk6GaqbbTzDcaqZI@)n^%cb*cvz18us+Xc!5eUN<2`n;)r|n z*$mJt>laBynPVWiE0Ry`vnKeWEO^`m&|^%RL=KK+AiX`ZEGO+B2qfaTaR1VivP4_R zRxS2vBUUgMn5=Pc0nbbgr6y{*ex)VWqqVyI)YP7tT+L2X z?LcUW_3X6KM#L-i>Kf&7HNh>PT-Z+(^mD6~RE71v3?Kq!Q;?jnCOsmc;#4O$XhkVh9^)D6 zT0g?{6?v0f-a55tW}a#E&I%;rdz*rJS}VR1oa0nm$CMONVa56QZi4sdjTMe`Q>MbbaC4+bhI>RM@MbO-{oK}mXdS((XgCR z_X#u2pa;u9G*8e12-CGO)}4O!XPUGqLCne7C~Z;h<5WN$IdZ?K;nVI6O_QEmMC|{P zsU4X7vJ29z-VaheBdhJ!^HJDtvo}Qay?Pmq1i7@1wf`W@y&=l#n?n7qdj-hKbI1!^ zsqLAr$g0+ETdisQ>yy1q$3KFOAqqu1{Q!i&@TZ%h9h|JjO$phu%IyH9p4QOEnOqh6 z)2zXvl9iw(S8Zj|zLHmWZK?+`#>A0X!=vv#! zKPy(AtoO|Hpsa7=k}^^j5IZTs5K)H?v&g1fubtJ z=3R;^%k4S?rh3ss@$G#s>2ztP{v5yWvRSQEf++pz{^_@!&taWd zWSQjCE;=6b?AbB_t-E7uO~zQpTP+_fq{RX9HXeZH)>E?1&(Mja{p5 z5RThlN;h*UEC)%GOnE9sEp(4uV3(a4F--U-QZl4)lQ^xHReiTEJc*5=cN%q)msE%F z5t_yjx1_79w=?+5AbAIj-R&$-DJVvrlMQ{Xd*qB(2OuGUL94RKpKwWxySA>q3&K@z zy7hzoG`!M-Hr#Kf7F%y<9Xv*Kd@IMkYu~1U0hBkpO8Z#4hp>hTgfA!U2Z9X9{0DlILPqDRvfus`iGWQ1}wN2Fi^LR5ZYA0|36Ln$l>;CGa*G?*3 zzBo&9!jBUC1E5=p>-WsX>s>@3(F@pW?xw7M{IU{~_-!+lyMP<#{VsHVbDRK@2h7*| ztg2tjf4&I-e0&_&?)r_W5xzHJpRpT%;on2#)mWg%MA{N+(#BWz=Y0dM-o>Dr!OUG()zYpvT?|Dau@ z(;1K_5h`=;!kd$)$G!JX-=Vp;Xc7KXuOi#a$c&z!qY?t|-Cgy&;XGdajkQR!RYRaf zS8r6LkoKA4#o`M7OwMuaBqZ_@fu|t0B+5n9?V=I)9|VT-J$Iy-Dkfu#VJ{Swr`Fp@si*ur~cA{qk34B>w{yy;W5M{Zmo_4H_y z;@x=CE>#{)G+DfCHkEH$RFLj$abJ=5>Dy2b04PFh3Yp2`y@D3tfHT$klv$k41bgIs zL5Zx*smzLFrU1!QcTqa2^zc#Zz7Q^{Q;McuHdzx17Ws{V_i7+sKDj0PkH4TcK|6@^;ErN>hDhu7z{(&`4;i z_n@F~SWz&3OH`|>HkXcr8M!4*`{MEkKpE_Kv4UXIkSLmkntfEKZKCY+H~*JRPiIfO zX7Vb9)3a1WMKu+@)Ek-&Gh&14>&+DNV=BkEL(@|}VQCwGp8a+;v@Q1}YK(D_=a}3$t;Y^T#mb%^WK`Yebja8w(co6Kr=1chktl3Kty%~zAP_%! z`!g-JB1grQ{wSC;v04mXp;!$$b3L$C#vx@2Pt6X(CD!oar=WKJ>h>=?_IfU^{(!Qt z|`J=7-`|xiq^i~p;pjefF zx>7KsX4h8xH+D7XT@-AD1CcS%jV}DQ!ni%S5tsUay(_t%@lC1n$z-ZqfC32^%4U~6 z;}mF;=%?ltt!b9pUt5+^O0tgT+o>Ru*n<(aKOproR&&|P7T$~5Q+ZRc?UXx}4Ryi9 z?#9#vgF%5R8RTI*wwiDRGTaXoab0HOVx^B`v5Jdlqp~iL_3;roG+~BBR^5!=>gKH593wc z9>K;wZoDaT-%Zw1`jiz;&IuoVGRdqekqmta`dVPEVK>U5dYR8dY5ZK

p+Nw7gBuvx=i){#_luJ27j(Ez`80}qpiq-GB!cb?> z#{RF@7OD7SXu0FTu~Us_@j4F3cd^ipPpMw$Td8FRyO$IY-2R4Pt_`T}$O?c`N)fe`yFk8r(Yq!6_+zGG1A=%XB}MNg2#*6YqP%~LbA>{0csL*1rVpuWXImb=5vU2 zq{)vmfQe#}w!mApDayWZJ6%R@{ zuI@KNG_DiTT3I{YYX44C01id6TtdUpVm%lOsX*0;K3(kwQjhGXlb?+&AzuubTIX)$ zJ}YJXIC&vF0Q`<#X`GO#>%wVw^#b==6f-DEM14?$SZj)mlnlfey+zERVQC+i7?Wc{ zb+cT2Q`Sz%a_L_MlluA@5ShAeExWRm8A_Wez-?=$`R$YFQlJ1NVJu~bEg}{m2>b1j z-aO+5BOYP+P)A@d+=ji#nS37shx0LtxM6_f9-V;W4bT`UmO#oeLf$Z?w3mE>48e(R z)zRKg!_YA^9GmvFRH#K6_fPil;ML%t%7=i3GQ$jQ zJ2hqi{z~_1J;&m93BFh`;okrK!%&@VpS|UR-<Bd5VRH>1a|j8GWmv6jZa9N8n=S?wuJYJE&&`@g}x^{9ruRHPJ`<#=eE z(`4-{ekK--mHW5YOZs-;rf^=gRTW``za~csE8tNc7;GOs_@}z?ki>kx@uAwOsS$76 znzS>eI;4GMZ%hi;nzA5#xsB8C?^>HCx0&6mi2qn&-4k-8E)EkXW~g%wPRU3Nd1%C? zrF8US`jK1PNzgWo(aj*AoJn2`d>i@%a;_}`xDdH|fmndQ_$09FAzfxgci~f%(6<*~ zJ>Wru~y z2l)<%8LQmTJ$XZhEB``5N?E2)BoGm|C1q?5I7+Yx@mI^p`1KfGJ<6BGdaewf4d7Hl zq&;*-u>aI1Qdp-jR;q=o2)d7MNkk1NY9o+$9I6iT4#ki8Y1!X9RtlsG4V{of(M+?e zcErtrxb8ZSB2NrOD1kezs!Kf>n`@t9HY{TsDY?1At(p^i1LYDSvkF-(clIP6lI2Os zI6|lOYIqE1cUBuDAE zzz9Wg)YAZLN3c{79v8)Y6AOZAHnk-xM)Aurf~MUZg?pBmPnCNwwka(LEs|Or)gzk= zRG!ocjN6Tf4Lyr{s=uWItd}}6BckYWDO`968w^6mEQ5^t)t(x^x&ex63b6dLnSKgA zMAsJFD)l6KGrhC1R{($lW=wBIt zk@QimaEr#IW|`{}huEUw-I6{d_T1g#j4~)&5vqQ9%XPvg!GX;oN32?}vJSJQX4Lrl zmS&O#KIxA+$7?AYIME|vI)HT;ZKclDu%sL1e4}&!s)Udxs;lAYu-wFwdH;IQ4)gaJ zB?0noTw12QiE3B#bz}{?6M}5WQ4E+cPRB(6|4h4q!7RW#7(hR#};E zp2!DXJ&U&{A+P8Xb5Scjk(vhWpElD-ARhH>$4)I=Z6p-GSiE5-b!LfaXc2@J# zw|>cQc93CX>ftaV!WW_>&z)v7k>P-#_y@zB(XD8uUL3!iwM;=_ZhyZ=X9r-W@yA3M z$I6bjFvUS>aOT|xXxC^8sbpk+5EgmQ^b0k(&2eWq$D_(-U$o$+62cJF4%Bm;Qa|D? z;EDd);Y|AT15i@+lT9}A35VKv9S4#I74I$qEp}E{ub?s| z{d%U@3$RTsR4CRZJ2^QgKw9%!WLbQY%)2s!Pa4wE&c(FCe<_95Y1wfk%Szm$WmhBz zX16+VL^K|!t&Pn!es;ESf|n4-@*dlu}bFaGhH0@%mUJ{IB#OcjP3m0z1ijX z8?fy%iKd>lahJl^W*T|8JBZp8igtB5)1STdmKok8-(Gid=#}$&%SB1I!g@vF+QE&3 zOI5X*Xb_j6?=B56l&>tYqVkF?YHlVAvdJ4fGiQpa^_GF#PaO$c=Gjup_u#Ya`#a8; z)G>7?j`7cWwqBvT2y8yLj5%pi4elHn0RTKBiscQok2q43#$p2EuNMBAAP}8gSgXf$ zTfxe?SD%7909?aRvY0Gy23|{J5=BBGW(a`CJlEua13U~p!RPHq!Y|-;4ZV@oPa?YG zfpPRZaiFsBZ}3A+3_jFkm+l=rYD1IVZ&aOA>@VSUF41Z<_zsn`i1&a4C=r;W_K6%zx#on8%&!m0k zEjzn+*Jn5RUx)HPplwe_y#MqOWD{o>CsRY)|41eFMpiH^%t?1b|FQB}IsV(qZ`Pi) zqiKiVKBaz#)G2%cwRj{JF!TS5vZWgzvVF~6NyGHh8&1L}Qm%6C^tV5xR#%lOqUe=_ zL^btC?@=@b@QyM~8roIt)BaPs^;-S;xdX6g%e?>3V9-r=@8?77@HD?)@8{F#ajO0m zsXZqk&Ef6tp`f+wad?P{L)WkC=dvQ%Q+WcncuSZv(@slwpXXtBUVsQ*}b>ri{vNjogKD7@_KcQi&xtZ)ZtoN6RcO^U^an+cLil z8|5L_A#E*F?f5^oWU0vj))ci%ZBV!D>3i;W%c`}G!d5S&%Fr6YgtrRvy5%I%Am;#` zsZ-9R3+n-4bUPiuPt{x{e1^k5x|7p3<0Cz6vlI7TAumzgb6yz5jYcI-+E35-1%oFv zmvP|8oCUYQ_s>m<)E7>sgIWp~PZ(ABY-?pwVbUg`E~RupFt8;%xl5fX$z_`}C)8y) za1veLJet@S3V)v(B6bJ**nX^0x2yoxu*)0F^F!{ZLRqHvKuZ|Hy*}1gK4;`l% zVkxr@AW<9R558ZRmDGl8&p-hb7VGR5VU7|yl^O&-Uso&<+Y!%;J{Rx0CF-sNsP0w? zVBr5?ZDm-$=dM*ZyT0Xj3!SAb-9puQu+>9O^M?h`-^y;VW??- ziv9q@g>36>+d6h~+c;{g2-Sz8A#dD&H)Cyt3c;noJ|0`b^}!L<5iMY2vTuX0V6}9n z-9g(c{FQe{-;)^1!XG(8cjd#lMH0LC_4`+FdR!p{L`(?;+XlvjSg&w`GkU<*ybkkf z%Q-qqh3xi@fyk|^w7DvPr?mbZHNROy;~6BauZPQWFi7+w(Gw~;|Hi;)e_pX6bp-1X z4d>b6(c-Xhw~G-LISs}vFni$F7H6&fW9TS8MBow6$<7PiS!}IgFav0ZGlnC>D1FJ; zqBSoi67bJ8l9!QDA=ztuXFa!sj`RQsw9O!}B9LVlm!Os_1RDq-xDDGq+sJ;aKZflN zS_Y~%_7xQ=tT+t3gw3^rB7y<5faHz~G97wY6ikB`&w_y?k$^MfLa5jUX$|PNH6YsO zU*e3A`(2DX6p7W=Jf5${X+ffj&7+tTwTA0%ZP04-0w~Z znt60MY+&(oB}Ekbw)Gcg=QqV1IR%~N3NArwr4gV%uW1gSC;U{G)_RrFb_%u${fiF; z?i&?Y;cT1>c$XNZ}(6bUJ@T*V3alOEfI335+W}jxT=655u->4 zo+22X?m@FHi%ILI>2+7gC_vs$Vj8}Y-#HlrFD!FH=!4l;KaKF`h(kYPLLA_QG%wqM zpKIHzzSl?4aLc{^BGN`U9ck_GtjE<&XhL>%F8l=iiBun%I<^%7_6xJmaJcEV18!DA zlW4w3L%eW$Jrb727IDsYd{?`G>+gZlT+SK)zGv$kp{o&DRXvRG@lX^Z7kVv&&Ngmu5W*P=4k0GbyMky!oD#yY3qfIk zh)N)A49=FVNt{5wyRewK%WR1^`c}OT0@c*;8~kviDO#A@`rev_;K-F0?`$TTR2Mo? zr1D7>W|uxez)7jOLR`oLkBo+|ify>EbJv$yq8qDf8+S}tB)abNy%o8ivDWB0Td5l6~L*Y!R+uT-# z{wNg6SIk7&mPZQLXDP_@>PLe=aB>e=zuNJrZp~!u9F*4JC~7GT;j3t>%zTc@LgJ!B z_Bk%_L7AW;n_xy|bAJGwFeq7byvM|z{pD({401mNPHO#V@Hk}4^!hz8lIaWybDkO& z_=kFv_VR8lciGZnH%}sms(gkzrXmR+lwZU8@r`})dBpw2&a%LjW8vKM$6*+&=~u&c zV3aeIIMf$h<>C1;_Y$dEx(hx0TE4=ZJeXuxGQ?B@-^_1!(t~<{*1>gy4I{?0!I1Tz z;pmCqstao1j@Km%M49}Tf=6xM#|+R940>;+`y_B?!#%AmP=Rlg|UAgGF(-;(RMPJYu;C{Y_% zO=-oX=HJ&RM?(bykWM!%YF3W6>P+n2`>Edf)@aqXHWK?=ALu=n*8mt4aUE~68e0uL zjStDP{N}{~%;UcPf5KnyR(BlFMG%^y`OZaI?g1~brP}w!n|iZeT7`C2d)zO>vu)-o z6nulEwYE{4P#E;#13jl>YUYx>&U45XrdrMC3?oQ@>kFgkKl)?4z&CH z>}6VRccKKTN;~$om&1z1z3Bo}oBJGsoblMr_~Jteyd+8Kw7BmzF6P{!u18aH(7(NJ z2=x=VX>s{KBWAeE*W`#^H^uf@NUdeR9>V5*DuPBe$cnHk+=i_!J30LAuMa(s$6YKN zuA{2hi1(raqjrCnG`OX}Pi?Pw#-?@f&+U26TDK4aN2Tm?UMum*7>7&)^*0uSKeqBX|E}=IA*W> zZj?MTSCgZEdbmr0!-#*tB8I!(j(Uje4MXWAqxpilVH_rvDGtK_oYm#ay=ec=`L&Se zyTkOjLslA+d*O4SAfA-nyVQ8Z*VljAL~8BUfGRMxq33G08!p<-fzRiHB4qt4QsfqX zaekQrU|z~weN~=r7_uKbfDtet2CCj5@T72jF^DShKapC2 zYru=I#mU|#5xAsI5rd^qiX5`dwjBuq_f~NLav2C7obv(l;RFD{?&(R%inw#mdF6VO z@0S=QhiR*Yn^ReC)hdGb*EV10VmIpU(v$1h6B+E=sQx!(_T3!ff4TMl{~BdYx?>>& zu(SU6F?y^mZU4U{EcGPQr3FPGn9LcJG>*(bou#s9wy7#FEz~*Khf1vY!t`DoH{5Z2jL ze$F{0!&VBIbClb^{D$WtzxRv3=+yC49G|;WeVXrgV}QC8Ig4q*(OtX_pV*O)EKq&K z7}T58wVOnhw|hQLOGo*0Zs4E}7keudqN#=hd4F{5ja9It7wvN2_{s}-GrZ|Auwpff zQG~4ywFUv6n_Y-hr%-$a&3+>*Wm0lDLcy6f#fP@y9dZ zJHY7JQLRl?Trl>R%QwaU+hyZ*OTfT|^&>IH^Yb!y7lnDw3c@a)pe-~gmRyO&F@(7D zk0?Sr1O(ZHsQ#&@0k`)|5wb{_BHrI!AYpXj1pJXDz3oLa#*^3zrd5{t20$_1wC&kD z*2%3aG+;tncQN(g;BE&3zr{08T?*ehGD~{)@#n`_!y7OnE2z_uJ&6&bRy)-Ljjv#3 zPxK{TE_uVs#|;(9n!WQhL377&fP<#z#OOmb+)ZLRRvmV`=uZ85F+k5y9BIcB>z7<4 z1az&SB{P$jOb^rqLj^`pkR3W>=CR0tB#Y^={B3t9Y+Zq>3>6I`uwL?>Bowg31*fq6 zAGXdZMzg5h)@9qa)n!+gZChQojjwFmwr$(CZM%!7_u0wG`IG-fmkTQgVC)jpeGbGRv9zCdmol zC?UL+VTGA|Itdl`8tTx!dO;9=rLPVz; z7dh>mS=}|##(dMK;?DC%M2`EL9U=fX!{AxCm@5TnQhBX0KUdwJ+QrY6tvNILZo_h`|nhFMey8sUh<$eG9+@- z+F_m}oiqi{TT4B;=S&RVNM_kx^zo)1Yuk`s0A>|Sk04l%wLJ*P(rW1Dt%20~vAM5} z9WkA8wi-t^jXr29d5yY85|FFZCu$i_X|yqeNlu(p3RBUh**{=(+@v82fYBkV?xzkL zSQt=*2$nOLmOHt393IrmNdMr(U#P=52KB6Bz0`g)_P413bHE^)?ogu)OdM(K6+ zpgTziXPeIlHa@N+E&C^~Xo9Ua-lV^l$hGv}=tqI-+&c4sjyMl)mEqfb{6tlmRruqp zl9}EnAHj86{RaO}QMKeO0L%!;%TrGoP+c*b&(3#51E4)>7oZOG7Wn>ds9sgCQJLm? zT+#F%Vc`w&V3<8Rnh4)1x^uz=6)Wi+m*)2pwt^93^lSaFO*yLtVV#sf`e@^6fj#`& z|`~^LJeUGc#JP+(04FiH2+hf0v z$4XE6!4?spbpC~1bQ4ZSh1xkHx(ywwq95hx`bcznP(FuLSK;hm^gUBqnJ$0Z0SnwH#5UMt7>#wJ^Z&52|L`M$%x?L7pH@3gEd>Wf-`Uv$Ekx7meG?j2Hj+vEA zhd^qj39`~jg9*{r#R6dC{SJp#S0iu<7tS3ily^LN03V5+yz6??^rtOmph4@!rSF&e z^`6X6Nb>XLμaAp9HAgaW$(>SBjfVs{22XuKx;eI8{gCEML>~wV26*;$%#E zpIHJR_P=xUG7_pD*34I)O0|bTob@!q?!6F{JtgffA>))9jD)L)PA&jW;K3oL<(E?i z*BXgyfT%Z6r7MUs8~R+Jkoxy(!;}vBzTOB9HhoWfG8Th+5(BJdH{EkeQ7PHT){9Ct zqkrO1gBqfFS#AZKE~9&{(4`A;NE!FyM1Y85UkfLuX)ZepF1nE3OT)5QJ4=d%4tz-< zCL)hs*Gob&kIhJdE^VY_uU?%n7t*Ow_gH;0V7cHz`FDSUd>gHel$xJZzX^&HEq!>K zbo!zg0bBvKFwl#1bZLtHrqZU&$=JE9R;tD3#fs|D2?~&ZA`CP--z2Lu5nEX|38$8mIvY>eGJvN8APQtC?{V7k8; zj;X0?RJ*1Sj*D{*GK@h43QEGO=X-L8v9%dUn8}9Th$zVOcF=?T#fh&1t!@k^5>^YJIJfGC zXlWG)?Cb^(T7fiE&5b78YJz!HKMFQ3zs>}5_x$EaPOH)-m5<^zoFSwO5Dxz$wWk)j zkE_IF>ga!;v>%ZkL~36(?4$jwy{^f{X?IwXm`}-wQ*6+})S~t~ZG8CEt>l=f0&cMz zog2grdY(XK2yHB465gFPD{^OrJu#^e(}+Up)-RgKOPwM}-JmcX#2pB;LU=X_G)Wmj zJRI{mmp>;AyMd2wHe0U)fUTdB{%56&&P;X2Hlia@F1|qeNcL;XTAs<2jP2;n%_}B} zX5WK5qTd|3T`MGs9zo7q*01Wfb|Gs|`o2r7ka^aEd|H7~j*7{juNs*?@R7Y&ImI2B zXdxBq_0_U@5y8DUS%>js4i3o(d0~(`4^jvB#iMY!4F}iu;F{omukrbFlLJtF<6>>)X=AdW zFt})I)=_=0^WsIZTQhyJE-|FNS-Z@D`=G|IGqryi(Cb%+JtA8f&_C!b0MUvTqJiQE z$10QRZER+3Wp&sbV8kcHqhQy7#~>KFI^sx-f3W}?q>)x9&Sih%_7bqvnGr69oGd-) zb%9KL=;**apvQ`Mrk~S>e&rjYx5eg~Mfb zg)UU}8f#Ln;5yHigOaqL)HhSIJ43qBZo+Cr?ZrEdrhQle*zXq%!OqgWBd9f5eJ8@c zSa8YjC)b(ShJvBJv&K>>LO!C8rWUlQSB^zFSJaj|3izvq)m?YAQ+fp|Cx48lZ%EDp zE)xaio#hqa5}l?=v=gHUciREw%mT%*v+dbFsBvdw!a%HHhc%_st#&@`9K=8`-&F*q z?h{)hVb)|1AkiY*pg#wCu83M|SNhi`vcA)qgB1B5t}I=0264T|>jy)8`oeIPhQcJd zT0uw$u_`B5f+X280;xx`Gwgd4VsrBr_YlKJ|1|-Ko5^c+@D~52St}&{^D98aFx&~` z9M{~OOSMWg@i;rX_3x{VUyf92!vm&prFl;@8t>5}%edY+@UHR@42^Grm(UNTs_LiQ z?7;M@)z2NF!oKFr|EC=PpN*MnuLwf<58C+u3&(4n$r#%I>tbF&>X3$e{@eYI1WE^P z$joyS#Aw;fQN?ij0U#@(Jy$r&^KUbm+V9)8N{K3>>7&HnJbe^V2LC*vO`&;brE+Hf zM8fy(owmMvg_z%!x*!0!FG~Pl4fAh@t>?G$i}L(~Xs2hdR}eOqH87;eGUVGwNdK*nfQn0p8c=$NByo&Rhx2|RZ`-kM&^ls%w{uqvghrL5mm+yYRV4=! z8HuMT1SZU_;L~u69ud+g|AW z0(VwbCn!cDfIjFd_7+j_TsxRp^+K#`w1S1V9_=N>8+p(@RfQyPt1MGVU@sC11*&!~ zXDABceecQXvT=G0Y2m8jL9!hwSa3%!U#H z#F=thiWI3U0bcbTi~1td%)w0?f-I`@O~&jMUaO7kNQG?}LZI5jWBM^Gv1lJ@+D&}> z9APLVXFjCel_liGj+*7}Z3Qs(?*S{?jNz#T;IHP@EKCLC`ROEJ8aNcrj&73M5yXaC zfVwDG5hrBo#Fs|qP1;e-1V317)ZOytv=UK#T>0E3%9oSbp2t|WH6svk+6s)~Qw2M{ zW7VxVtTc%o&<3NXI3Fpvk3Sj@sd5*Hb01z_|Pm&rKIYJY1syiMyO;sGx%LDr>-J&UO~{?>JH?0KBg} zaNgf6SZb+^=VUtTqT5Jw|C+%NIc-H5?Cj(|G|+4MyHV6a2qtGO_(GE69QWj6U(FLX zCxKhA?Y2$6nS3-eM5A$aozXUZ8&m%sj5#RO?zlkf(H?hC84ya`QA=s=d^-4q)OS6O45wj#MDhuSzT+mp3)B4(tqtL z9}0!kB1dfmdT*iMi5j^z9nS3Raap-N1$y0dmf(;&ZQ<<+Ypl&J*S9Y*t%iI}bg66G z@I30%Cr~em{D-chNctBi0MNuB@^H;j;5WfHvfSXayx+VT8?di@N)5kPy1zpmADqzJk3 zFZipfNIjvu7c8KbwKo?JPx?b(W-gtHotvAyERZ#(ELIuTx8-B|Ep)D36wWS?o5gIL zqHLvWkAr&n$$hfE0AQwZ{UN5Ou5%ny5n zu1eB<^kI6kmYlcA2O!JdpQ?4Ruaki4ez2`*9Z#>-Bt@RBx&Zuk_ff8EZE4(H)u%zp znUQXU-8#);Z$wgE69ol7XMyUYyKC$c)pxhQm3{#`u4fgc@c?fD>#C3lJF2|3wuOM3mw1bi(?8*Z| zx&*bbf(n&f0PGJvj2aAh%L4$=@NJQ83P$BA=G!^^rih&;@Fy)hIlABgPzN1nlA&ew z>eSUokbNImj6i(qH-s$ZdL}x2;O(K*spy#VhzYMUqI4iQ%<{Z+`oeyZ?J|Y>qBz+f zjiq5$ppdDhb`pQJyllb945`*hdnkZ~KzcMsJ2}rJgu+mm-9S4E>&3+oOUelq@lOD@O7-9q#76;3Qa@C}QJx?k z!DQmd;F=UNjd2--q(^D6gE7r7+8xh7*OLLL$kwpH7fA%dnsVEFq5>oMO!^3eb=yPU z{r4d=;KuCvBqZ8`0eqy+bLKqKlF@N&1=XSz^UX6x2QWD`jOE#t#mPnGj6-sh&w z!3DsNv2By6)&0!SdHL_@vm)EiQUfH>qtuBx01V*#{dQlG%f{!xpuZ9clM%2>Wi=>M z3vV!9d3CVGR3-m9)+Ex)a{FjRL0eie49w9_Sq@Cq=~g~BgPD#od_?rIpAvAKz}o(T zRNAJI>ubBQBD!{YVta2#&+Z<+s?i&yLRlH8oH75N8wi`QA=|c)AsNJLNSlz{ug9;N#($iv7vl>L4c6+Px4LVK z)_s`xh_3dIw4x6ITd%@OtX+`s#t&kV;Xa*ot3A@y-=XzPeh-rFel^OUJm2yA+$^K| zSA;Qc790kgm-Dg`4Ev5A^DAS$!h|gG3P2-U0v-RNBwLZ{%~Ve;{TxR_Z=!tm=mR$) zUE9^4NWx%3pt2-eoJxDkTy&QN(=)r3`tHBJ1$7ZDPe+rZJPOZC>8DMfV6A-JuK8IY zf}|kvzEWM%OgZQ*5(><;sVP#(Hd^B3I)fXaGk~|R>*y^C>`gAtAG#LdfQwPt3E+YE zv=}=33|H7Ov}wiY!~=5JP1#`6 zK46fP9f+;A#Wilb=_74Ke88j?XTT#x)oJwj>D~-)=Th)Uq@jms==yxK#%h@MaemzQxTZWfHI+#ZaP5xCH+=+@za79# zt=Qs}d7tnx_oS(M?AO8WT-k|9L$AiY5>a&LO-_q@WFrn7)mGC*0llCh$e6*6y)PUQ9XR_EL!BX}l*6X502P%@~dJ&?ox6 zyA?BVxta}qH~8M`ZMkrfi?lZlT`qrtt=G)w1EcmNQ>~!toUhxuOs>2v=(kK8RS5Sa z)VMf#a-QhtmkXhY*$5UV z&XuWu-U7ovWIq>1e;RR(sD>j+0V@6X->Ag~=mrlx382{(3!;4Q*LRZ` z+LZ6T-`9X4U@qVPqCHrdh!~0fXPJ^W@h>YrfQ98h-7CDwe;k+pZJE|mv;&S7Ln6{N zuuGh>sfX;`KEyK(oI3iSn(1&))#hv4*Fm2CsBH$da1!bsk$S6l_I|QELK!)^nUM8R ziys~k-;vzYcdkmJ`&M=c0Ip3*?@t%V=Qr=y@#E9t?h@LGK`|ivHz3`>6@=pa&XC{7 z_uYjy$QKPH=T3QlRuJl=4m8j{x%QCnGs!F9+V|sg@@aK8HBS^ga;eN8r&F0^);1iZ zepBKU>3|@J5g2eblk=lDm&pHUO>{y8qO!t4rDE}5945IYEXX7cF7oROhTVy|FAX^` z%8xA`8M-let`Iu3mH*|O~P6oU(Gw)PyK#<_$wERz+g9b z_j9XtT>3~|4tH+MAuUc&NBC2vaj3MHu;kZbX{)@p3YcFXGt9haCPY*p+qfr-bbEuj z7=_OggB8pbJ4KL9z=CeKyi#GWWUPV{hgzIzJYMuZEj%EoUZ-8{aY1(hFi@oK{KG0p zr><+su?bXoao3=YT{=uFI}|)5)*+N4+>$u-D2D=BX4SBUC?&ekICD?+CkU%R8yY3q zdAH4PZb#50GLer_@(F9&jBw$QlaL6XC?V$1+f@yMZEQvhJ;u6A(kgNa)q_}P`N@l!h>_10yUL~Zsa3&YYis+$uV6GJj;$vWr=Ol4hh;tB8 zS(asTjaK;-7Ol?CH~X$s+w`Y2N!;Ie%SIp=4+5YNKF~rQVoeWgN(w$jJmz45DN^4X zlOpaQe2A6|w2;I~E`kfcoY}oBn{}pO0K##|eNF-64#zjaj3_IjXg1fFCF6)N!q>us+#XpLKUv5fB z)(x1RQ6_v~7ME}V3O_kRbL@zF;_pW}Wt|}FC_HYG3^jFpMOT&+D_AIsA5kL&{utx( zu+ZWcEwLM=Mj4l3K*`Z5us9HAB~kkvN*)zFVqz8ctocZ$$w{O0s~-U_h_gumud|pG zLdNA?*ZRZ7#ps!FSLQ=3xGY0ALzK|x`~c|w!zfz>E0Q>de86e5#CAkppLIWP9b-q4 zLh^v807OtJK91Pju{RL$eWFm=N^2R1_HBW!EZ^Kw{Efq9Wr%ZVn>D4Cu|~?&*a}Nm z_sgm~-1EYfhC0e7(8f5Tik!R4=Tnk98KBXxV5`b&th4AfVr+pRQeo$g#Nk$ZODtC7$jBwi=UZt|0$AuNqdHO&NsrEmGkc$hOTC{~`#K0j-25t# zm)D(-tN~A*`gM9yXWi1+o{V5gtqy?d`KnnMUAXS9v+|J!k5aGk5}Khzkxc2&1q1R; zPO5ZwQZW+k?T*Y(HdNMkq~_7(=W409S)%<(Y}mPS{HZic z52u1muT1ZE2%$XZi`l1#UO#nl#=LKGO!ANSP&{W@S`4Co!V~q+rU1jG2!}PTqRfc~e&P`Ig)=`Mts~=y z8t8x#0RU-TD8#Ev&Zc^g6MB>GOb%o9y+gA_?8a?Z=3@!ZO_E}EWddmzuwn=jt0m1 zb`?WJuaAM1<+d@(&E zQqZ8=rvqr<8!D&agiUB}sz^7KUT!A>P2SsPZ4tU-(FrdUL1shbPe6~98Z=QRiZN^8 zPqfIIWX+cR6LVJNi-CpkuD$z0i??5@2`Zn#)|U~AFL4+%SXN6?3oCm@5|HBT7lMlI zPUmtASyhL=6bj;OdB#LGR-J_yk{KdWRYhvqC;>=XaeagO)%-7_rhos$YsRRq=+JEs z36J{5?@M`7W1q5zrde1o03_Y3DPLHKIe=1m4nvXmD4n@77o`` za_*bpb!hR&h`gTZp1ua;vfU(Pgk+62;yU0wK|I1W_Vq42ROsKj7iSY*`y7gC2qR4L z(E$kVrn%kwq6JQ5jhJo83=m3P(%_%Lna(bsV%-;BO(pdKHYwdo8{_RyrzORj&bxYf z>J^zI5xNnW?cP6epHr5?$Q(lO)r!r4lcF0+d~?)ZM`L`%m=|$gH7+HA2%|@F%RdvY zGcrpt0*%*N$14#dTv9HOpo2k1s=dtL1punH6Md~_Wb7&?*_93Sztmo=ly2@vECt0!VONe2jB+JJ=s zg4KiW&IwRZ7CH;gg)$f6jxOPjy`BbWlvOiX);pEQcf4;~2@HuR1k3zE8^~AqFa?#T zdQ#+#qdmtGl^X&nl{~E#NLcbov3enf;2 zZ*!C|<_%uas(`I`q=Qrj}}9N?*BHzE$D$x~?06f7xh85xP=Zc7W|RJ@8MoM!(b z`@LRZim7aHlj7VA1x{!f@}_j2Xl$8*%H0i&D=`CQ_NI81ljnC`p1|LCpO(c)qElrf z?@z70V>X}H zvZsK!&2?@z?Kuu5Pmng`?QQm)RIul4PX|`P>pMMG!R}OwRj}q#KaAUbR}~3of{9=7 z7a?f&-RBD(%8566UcZ%SumCzL0fh9ta~vT)ZAR{AP;F-JXVC4!|MRuzS{oGVb}>AE zH@Dwy?%wwAw!67s25tD@c}BE{1M>yOHZO#fI-2P(&VaZ_ zSXx6@K?ZP5FZ*4wJF9K7l`^if1>V9?wU+zI_K4lHhFTv>n_E0UQ+8MFtF6v6Jw($% zKT@Phed6}yLEr8nJp{_nizix#>N8L3Qns@G`M!=%GbCYUidfgX8gN;3{`CiB*(y2T z{Y3fm@eT&&oF;bTT!oWfzRS%e?iXH_P3K^ii1M$m6v!1mG=<%ZLUa!3)EctRM8l7V zOY9ln_M#+`o_JHCyji8@$u6%P8t~}9eu=PLbF`pm!O2e+HlEP}8-2yShkQ=oZYBI* z=K$Kp(8~X&r~bEHmXV9;Kg=B6$(X}7^na1GC(x5Sy&nG%Okn1JUQW?JFGshgV%->n zFZ0v)=2*I*?4rCuH_1kZL#Ks2o%eNoFm&kr11z!Vhmk8Q3w|K<{ArU6+^@9P{H&>N zNABnIW2AoJMTMd?6cWe$ zIKO=C?Ug9cEebO8RDbe}U-0D#$Ume3#Ganv%X?qF<<|bBeZM^8XsFZ$iB-?76(+R1 zPC^~s`_0TFzsgFXo(-GH9XrLIZf|(MCL?pn?12T$H~!;Ii11{_i|fckr}C-l05F#d zx}j#OD%KfS5v!72)P0SKzvB20X}h_UjCizt!g8W z_}t4SYU#@dWhX!LTRo~g&^~O@4v<(PAM%{@Pei}3K22gHsKIepE{k3>x{PLq*g72q;Fc4E<7czp5J99_bY%Ak_3j+YLtbhE2%ql1Dry$V(61c zdSRf;;rTg>a!-;}3nO;dML2VB(*5m;jfe zMmr+!ZYxUS;G7j5lURp#F2mC7xHEYv1&M-TZLe{pIe;&osqzz>ge;D>&+*1g&chma zsXKXcqWk6hBn}iqY*t%(2{7-(Bdnk~)Go7RlElBtx@tN<3vpD9E-jYDxS*m|v?~!o zkY}XkZ2KKCdyHu0S;a_;H+sPcmx5qsQQ=i4D)FW9B}!fWp3O`1SUi2pFnY4-tB%@1 z*DEvMjl9nPWsMbb6k;~XTH3>AG%Wt3+bnV4$U`v3(o@v<84)ds0m$xstc86*fxhpo zI9YSVE`?65Xmb=XM}x3FlrSsv-F!51^^h!}eMn!@!q5+g8B=wbub=1Z9^JHgF*aJT znn&6k7{b6P?&@}Q4Q>O8qZ6518=Fa%Hr1IVOn{po)UDy~V8!FANNg-qDz9%p8}bo? zi=;XbBtpIf)yPk#2GnTj8ZXw$2nf+FXF_R(mHIi73JI)f>~ZH~^^H2Bw) zYrJj4M>y@lozU^xXz)FZr+kd-f!}XA=1VMY*}twt=!dZC39um{-P&^ah?bu(f6(MS z9!pvuZ;&Sl`>Ozyk3P~F>}xbT*-b60Ntt_Glt9X`FGjWf21<(v**3aPV)1g0SYO>sV>erE%Ic6E!VxJ2Jl4qNw_!4Gq&>^31_L#YL zR6Jnw|9K#+^C&ZLxq8*F)!TW6j;c!vEmuq);L4-eP$W+B8CNEk>+O}P&6;sdJQOyf z0#e?iS@4sKkK*>gk)E4~%{=jjf?5K;qlJoKj&mV31W=J-*@7EUkdfvqp#C}6fiAg|xwz$V>`MO@cMy>SsU)HDQ5E zgdtp01;}neMH=N33wTOlZEh?80e1;CG?K8T5-y%yV}md?ZxCJzF#EmQ?^CNY1#uyD zE`IbU<#c?h(4k(8Mm>adLM2N6)(*~WyBQ*&6vMaL&s65{TJdiuI!;tUNgk^9I3!Xu zbFAu`rn5Oaq?tr7KGY+3OLuA!^cm|tWYN(MJ^%>a#FBNhN4NCPjXLk41aC9OY=uY8 zXutJ3Gpc9!TW2D4EjYK6lkp$7ZiUU`tWKtGkyJMAsc_6$15L7#OZtNjSUt)9!Two~ z?{_wOlKpkONjWRi(ByjN8&K(g<(xLLHo*2vx8b1B@IX!w4qT`L_~Yh4S&I(*m;%@% z<^Ukhvd6nZzbPXuukt?xedgOth`yyar@XJOYCXKD=_OM&Q#l>wO?86y=l?)6<}~3Q zytZyLOE_rF2K#!<%_(|dRgpD58Aj^z9)^*VlivrH?Ulyn1&}uT&yU%wQ#Hl})Y(16^8hH7TBo~M zD#&Kwu*o3Cmm*nQz;MSdvtaTnN^BURtcuPiTUutCn<5&-@i8;CO~vc`g7=-%j|+Tk2_2VBRQAU~2nG3>r- zm-FrK(E25GWx&huil>8s57VQc50lln$@8E5JRFV_RkiR|A_!Br2z6W@e|SIt@$MHP zZI-CX>*m%C5ck!i`!~&LbSZvcwMUqm4mX;|^OcuTX%m7VH4~h~QQ8ODY(T2xRt9+n zOp93)X40#U$AyZj^0vpTa2MCtQp&cB#}Q+wx3*iCX2(`frkD^O;i@`Pvz%b9wcCxt z-b}tpSViG$EUMP>V>u?;lB1Z!-$>p1E!1TUJ#WUCAx3;d{l-UTLm#c*a$xe9uS~$> zo)gs$aK^;X7?~LTW5n%_0Dx?y0TE()DZe%tAkJ8vK3+5bL6{PMGFtQ&Itl-T@E2TW zbiS~Z!&>NIa!3+uyC)vT7VXx2TxH<~)7B-<>Gl9Vy^D7pY?U^)QUb&8>Jr!w2Pi*x z9B(ma>Y^Sr=XrD%D1MF$a{lF0oF_T#2)45-yix>{cF;2f3xfNOOn|nvyMMY&9xUr5 zd3aE$&*|}`(gj9TyVh(e{VPQ2A@vtDrHxrkeB@YlA3?taJJcsD?IQ77I=p3sSHu$= z4WQQwI10HYk}V7|fS^QGI$8uO7wLX4>;!CAbipNz4D6y8!i(!Ba)oKF&a6a0@~hCm zbqY-aeRJ;k8J9SL7tm-o*cMdZ-xx0OW{*y}o#|poM7@6xu?lygu_4pxTqgg!ho8kl z^hkq`h(}HE>@TzzPvarP6#BY@@{Mfc;R>Et-62Fj!&RH8J?+24<>#s`p0XkAv}sbH zi|3YlTO7Z1Xwa-7(qE^dc~AKUmG@W_$nl$?ctktS)5wGbfb@J%YXPKZ$55ItF0|pH zwV^}ofqnVu)NN%1mC(jlyV&EYT{x#Su3OTk)GK3QIq`X1_X}tb{0(25YUhyrCbmHv zwm6JcFm_MO(?p9#sC2Yh1s94}Q$lxG^7Z6nBrz%WiL68FjXEk|1@VEb!x7S#HhIDe z^e+0YOKYMX@ILqT;VY%t-r0q|I`iSS)#`52Ek>)iZkoT<`U(|`TPe+`wr}p{v7`-K za1&K3#t&4x6S@O3zyr`A17X6v8pKG{pY7hv?^s!i-_u?vkXbdso}j6F>Z;ZqC+#}g z?76y^F(DV!xQi|M=;$(9#yRbbyk~vU{CdLX{)yoMban8M6(Iz}z5t&>SC#e*IQn$< zjQ$0_E$iJQynO*ZjF)nnMtNymKY?^p!>>cfv+S`lZeL6*h7eglRx`QST-WZSkKo97 z+DRr9A2fZR*$kF6JDF~{2ABHYqu9^`vr5f8;1aUC2+{o&$8iplBdBT1n(F$ddxKqm zjXantI}ItP+cV5T0x7I<*cc_CuQOW(P9X2FcJ7~w_Hg3zIYayA(e1lF7sEg5bh0%a z1o%7o1B?qr9{ArBMXI6*Fa>~<kV9cn1g${=urAjY6aEt0zd zcpJ8E4C{k8R% zzS{&R0ieX|5%ccixt{EE5D;h@n`36GnFZcvzM%jIUu%Yt5xZ|no^Zk@Z$Pd>~}9B#tCs_->~y)VO{Du)YUF6usVe$ zXUo_XH@(ya@NDW(F$Si1SY*h0kFO0(6^T|=0x*_`^H$KfKxt+sG45)ih|-;?telu* z?S;KHSN^U4cvq6rVD7e)g7VK-Q4b6u9eJ77Q^pNs%p-_{5CgJXDa~tLJc6$!d=8(} zFLGFH|sRD$Ti#FFsX^71Ff9Wm}k!Px6v*_PpA$r%OF*1VW`)Z$$!Fg z0brn|Gg4pRn_P_%Be=K=(aX=#5M1mt4G-U1Or?#F@46|Gv&<8^Yp`wi-BQsj>ML-T zat4pTNDxC&Fl)wkEbL(L^DfzMy|k@UhEdiJaNJu+jxCe^E`fb5&iad30ojBK#RV^W zE)m6BE}#s@RtH7AqQDHE?3KJtvw`HT0;pimWd_O@7zS;%lF)2?Vp~l1hn?oAv`u~e z&(s?{aMcn0&B)fLZRg`--ZjQePli2ETCELy=&X!xbvXR_T+xzkNh?uUeNc35JKd_; z(PDM!P;qqJx}C0FgF2tfu~XRE5I<$In!y_vSWm75(9%ldG6mSD-MTzJnm?h>YK?cZh!QgvC5OMU~nr}lU>^4 zoJPP=DkOhDkSpl>KUYz-X9zfma!v zD@){p<5bf)sCW)N2JoxP7Bl(_0G;5QHrm8wYGG6h&{xLL{H#vZ@kd(fva#a>k?MuJ zl42^iTfWrvXNoXbF;5a9G#ffmL!|Lly)z0?BFdadp{!c$*MiL{5TipMDfp9z2*Hao z+MHp+Q()6Jgrqy#m&ZA}2%)_~?RBtYRnDXSbDgAQknA(9SC)bNg^EBT%Wmz! zOF`D78vJpBYH$-Ho0XmZ{tp&~de}`%YLS?ORccaRpcmZ<^1$rzB4A>E*>&$By!G#} z26(hjld#g<{S8uq#_Agdz-*5{I*khM)(8t@AW6}3+mQKcT{O5SQuhpgBPaO`$l`V5 zCsQ+=lXF(!qOrIRm>1*~e+++4jTloEyET7-7$~?so9p?Rv9@yqc@9}Sjjwd^JAGFA zelU+JZ~~$5321Z^!rZD1jRj=f7Wh_{*#cQ#tqQQEYE7fbmTOWTKqGkKicdogi!s#- zH5gYQM46~?5lMv@N7m0LUX);;yt&~ZRQcdT7c6OBP9SybI!h5cEMIkzDGCHk}`d_kyNtQ_S zLte!QR^0rJ{1l20-amhR@?VMc@Z4|+YOd_Yi}lt+(U9;>bT?A9bvAz-hkb7m7jTrt z2C#+8)|>L+Fve|zclmpVKZ*WwIw5&Cw{!XNJD8dQZPQJ$6e@PvLm{VO{5~h) z>uUASkg~C(2HbHTKJWY+^z3eL(!`ySiDAzqo6n22AtD~NT5R|ro%xtn<%wFaGo(iC zFc?NUM90r!vv~iJWEuNI}m;Ove>3EGdiV$75IR}G5z#78^cMj|FJ64Jh z7h>**;~0+^FLVEU%2yvYtjLu?BQT~7`^zDNAw~Dg0hD3J*^)Y=i8#S3lDw<*DxxVL zEUh=Iqn@XRW@k*$Td{hvbd-7*p&MCD3D-v`O*`l(SD7xC9#KgnqFvjo0ASo;a#BCo z;$N2q+Ng%OgpW9ZSWOHdY$|57wsZ6`Co_Ud**JNy3p2ZNJ;WOu8#twA#Z<5#GXi^y zA#MwM0J^u8SBL=0p=&>L&;`Xu>3^;+QtPP4-ooR08E+yaeqXO&s0UM>Ap>z@o3xEm zvJrUE?q57mN{FIU#(>058f5!KdnQyD=hhBJTKZ;$2Hf9H|6oJO3QS>GR3onRHX{h~ zZLwRGt;)fAo@N~m-M7z@0kk=za$gR+?Y0b70CV+bHReuQtjeGLwIl=%aQcbAbL_>9 zXQ~dhF4K#kxG1Y>UozNQebzz^=x7Bzn*(MC>C!Au0jK7Q1rx;yrF3}-3)^ugDSK@T zqRWn*!-1Ag#m7eSYsKjs(o(!))5{e_%8Ge2`^8c@=sC@ZE-)RD*H+zKd5Bx7#5pTD$ZBY)f*!t4%BP`{0Mm=U{Cy&41kKkN zL|RsN$-Z~d*p~_GZYg@OWAv9QrvVs4K;&EynTgay^l=^xy}dc|oIzTq6q2)5PY%Ob z@Qzn-Mkfq9yciAXI+=%MfQ&OMX$E9nfPVL8OqmL(I;FiWWY4mXjGVgMWz$HgNig)| zVs*-{R=OQ-g>|*Z+N1tTiz}z)Rg(<1qZMW9++aqh)zsoy1N?Pf;>B=3hxDcm0DCUF zgur>vz?1Ju2r8Ru{*$yD$+2rgE#S@<&|~r>5-4Ia0z~ zi^NNRpoKZu1qUX+o&{0F#NCqbxR5pb?VcV5Rn0X9{dPqpz`X49#%KMAyxASrlL&e1 zmN;VQ2EDBpBdprWv%h~Pm!mZakfMPhUlJ{&G3KK>1a@(@Z5++yE?w@#Xx&#)lE#1R z?&w{H?5|p+^qc@L)=dv>E`uuuIPh31YCM?$bvImaw5;VjFt=i=$d;yld|7B154&7D z7g?g+LHT?}sz0gK8`yu82%-9zTZ~*Et*#W!n@dHk0g?P zayj;96s3n8Rc@S&3|HS_K@4!&$a+-ebG(MD=(ICLwj#^@QttBU^=@HftRl_j&mBgK z??U+MWd;L@vZbCmS2@ec;j8#qOj<+x?;WaY()qZfOwE=kcts9iVq^c0$i=j#wA~>)QqQaU8Dh}n zD=4Eg)tqi#D@Hpy-&#b%B0xFtpg^z}lJ)WEe%(VeB@uwe*LAP4Z1yc~1FG za)_0F*KiM4=hwAzTG4*53&JhvnHs6tXQ-dO2+4H2l!RkTc*h-#p?c!|<|<#aM-+HDd`gDB&gn}A`B zxN1I6MgFhyFEA@EB!b!6HvmM82(R}*`{z+?V*@yh_h#+arhV!|5i?^?RDnAzRoESv zAegN}<3Or|K7m1V2<7Us33=Bh?hLH6Si&|%lREoD>R;gIvG(;8miFn&nL*U98@8Ca z`V^staS`0LV*cb)Kch~#Yh;_Iumu+3nmR?s;bz5piT0_@Djw>B27p=QK+85U>9`Cj zRuF@QaIwUp@lxW%{ld{H%Ogi!fST<}7!Js|C-_>WHPJ`kD<#>qCO16yiso-C?(^L8 zr7_UP6;=w^{PAelq;Qms_rI*-f8>y{;K0uHt|Zj?u#AaHsQ1-~YfCn{kpC3py4pZJ zxnVV-(X?lS^J+6{_W>rbRBD{RF~k+`@1g7!N@C>8Q(;mV!04w1=cKJd9qsVhO(UO& zh4)M6c|o^b*=*%~8>ltx`j##3k`&C_QI3#A@A5~>F4{tTDuwGAMd-TWyV(W=Ww27g zXRbP{BEHJ#iAg&=OoF8r4S`yrIe(vh3f z?WcYd2l&1R-!>y{6rn(n#0s>~6V z#J6}H^E$K2qY#&U$qFF zvuY91B>fMCSQF>6sXtHaPOb;oD=FFb*NxhjOBv$0TX5G@f0Z7xqPuU(t^0*&NxKZA zb>%pa{tsK<0NhFRtr^?4ZQHh;iESId*qGS1ZDW#5Y}@9<+Wg9S&d;o^1wd4pjcrd>kQ*O!)26htyWKV^m?*omS`sxaPw#+c3ZqC? zU{pL$5M>wH$wEwt%-J$J*{v^IXRR)A?0NE{H7GzI6Hp-}*3t%ejN6ArDN|tst^?X7+VK`6LPy!xYphhRVj$u=(S^4OL&j5UEv$zljmqUiGq)YX+@`Q;2ZFcx8FE^ z2+yx(LSyQ(c6cR4Krt|Dt53{UjOfz}9Ybr3=9JK=3W}Dqd zv^^e;gG%#oL^bgyZEkHSqVY1h^2U2?JYTJ27c`__j z7x;z$1(}vy7a4!BJ!PF@F?S#6KyTA9_7MC6fVCaK{PtWSJq_KcJ&Nd}KKRKBEu8gt zNjC_cTNn1nP0VVj7CtF?$4(7L|7})@xJn3L^DHaa_V1CWH?V%p4OO1t7j5OT%2@LBv&E%-;Hwi_HQR4`d&kp_i|vzZ)MEs$u;+g{@5r=(&nH0_ z_~gC+k2>LCNkAq8FtaiLkCLKCTCxtC|D{fD5$T3F#ru(>KxzUvf4yKF8rqmW@1n{3 zobKGfS%eigciH2tMr;hODd8nPyc`S1E?StS1PWF)s&b?Aqqq2i46=U^yqEhq-2x(z zzv782^Xg`h001??%}NhJFLhUGhKdbHfSDVIMh9FLrJK)(qgjkz3^eU$cb*=YPFxeG zCKo5g>+8Xl&*Qh8XrQzc_CYZ8GwX1nR{=uB~JQk&&~) znE`0S=>$@=FqfH0=o~@;{_GB$^qtH@i~x zdD?9*bQ*Fnp_2$%^2&Hx?_V#e&2xpxJ(mpSAlEU&`|O`xo|{6zE*J>k9zx6$@j)t4 zjv{iS$Tc6cLg7s}my~eT3J^1LFspW&>i~2z#Y(859BNg!cnEfz2u2n#t*>+><>5h{9Ymljv%Z2rCE3$c#ECQ2VjS&6#XIG1Cw!Uos*L1Q;u-c0UQIe`t&I zN|Dn#&8S^6j#0m3164!pY(H=_yw8~`?QD84555vGXz&|l-gzsX)`DLuIoTr-m3ml_3EkE9;25iO?KNP0%Wm6=WYbDQn%+4`H!P%S2^%u@us3>&$8{1~eiH$A$Rb z^E|gcdja|dpmgXV9zu}A%Q@yGy6kC)vL*LyuVc0MoH70BNU?@>gKmg?`MljNDuf<% zCm8ropdTWeoHw{6xth~349tevXEH4y77@BCX3RS>R%Cg?oK?53pM-@i2*=nERW9r( zEI1w^HK`yF+}`5!PhuFFC@3w4h8Yo>U93{8{?HGZ8-9I+yLnFx_8Q3joYu_tdgG(F z?mfc&ny7B0XFmoOiU!McSU>=GdJ6#`uz@gh9v0c2WecKHEeN!S}v1E z_5fjkR?H_?u<`7Uw-f7h^$MLuYdNVdW}D*=wwdLXB1_c~>@>_>A?*?PJjt^WQW1G#%t1DFk*&Fm~v66JvR+BG{{ z{bczZIkCxQ{2K98IWL5z%j8O&&oj1Dyly;pqc~}^YBTDP`0FA^( z{Vv!O5!`@oSp8^XFo#s*t_?DNs(I_GA>9B#CDgP^3Y@!~;lq4g+~JmI=eaQQ|gP z;(~pr(hd;ndZQ6Br**h)KV3w8`Z zZ@Nbpd zfJg^T1m*#FTR5)k!|(mvbM#&%$?mv*pVEAP_&h?aQ=Dmya9O^c`q~e0+7UYUjNPJ% zvmm@D7F#!X1g0{U|-|aiTpMRFJ$}~5chU@`=Iaxx* z{4bwh0+H0u$hw#B&CTRJz0`;K8g$U{UA}eObJ&@qXl{P>3 z(tX3DL7MvliIoU{Kjmj%&_@EqmsTWgqlMcgqaOB>sXqFke&@*uMHEPC2dUw>NQ?*k zDr^Hmb<>Y`J@)4~<<-VQ?3CY3;~ab5mZ`b)#wP%;4>glDcpvso8#qJ#yQGw&<#w+` z{096S4q1||uZj@e(W?P^_Kvi zgc2zweiwqVy;agRCT0?_Hk3J=#E+tS8BY8W4iRF^O#+JUV~dvv9>73N47CAjAD!fE z0Nufld!L7cA)qx-{zTM+_!NL9OpQ2G(_rxGSr>toa>K&Y$x@@eYm(0_IdXD%=N8*_wot~UOlHVa&Y#0f>~l$LJ0FDtZtxS zSB90p#!F7=5Wgd{9=BcR5O#!6{VwNw=?cC&Vx_gQwtrcS%K60?@~=nC$F5AYGmT}Q z*N2}H`RZB4V5SgoK9t&(H+W!4^Za5P7|nZEJF~XkVbpX%#*}#DxKpV&!`AI_HG0E8 z#^UEgxNb3ysCfb~ci0zLS_wdW+dpZ7d3(-iT!RIFXPQuTApG!DODBV-AXWW)udMHs z9~8CLyLKMz=TSoSkDgOxx?Ci0fd%lMRU7|W+SPbW_#zH?r+lcq@-FMp1{HuPKAP(d zySVCNL!>c(AA!osOX^yQm1jf0@%VLf^-xjXvmPE?UKsB1N%cq#u-`kWQJ;_A+B?cB z`&|24I{uRd$h%_9%l+zo_40YV$AJ^~4m=eNK{CsuF{-lg*&L6eBim}>@B4bQ`SyLk z^nL%SyKo6ux-Jy-=~&CX;jpmCBEns?{f-&M%VwkE_o^Bp5OV^FKmcvgk>=N;8v@uh+y@?eDi`C@S z0chF!mGjlOqbK0)=jFZnc`@bw;OpV!?bO<{9*_>GLo8GDIO!UCntQrEb$*|?9>=(x z7^-^sBPQmf;j_FKW|-hSIQ$}D?%eWzH5WbcnYA)Bzou`iZ*ZO0!_E7PzjIyxtC4jf zFIWGgyUWvQ!O@MY$J5>W)%N+l;!pNe9|NJ0@yTOF{Y4J{SoHixS*=LAc5i&fW$n{Y za6%El4VUD`Wve{I&^GU=WRVx;eIIjx%Re{clu$uJzt8jQ?f#D{oB%&wYZ_)&UEA@= zP2cCed4JxFg5Qm!zx(>@X#D6?-;Mqif~I2ci#Om2io<|Fq4(vw-fzjr<9^{7plD#{ z@JLQz&IUg~j-H0F^5e?vr1!pjlpHvdxNQJXHefh|)Ves~D<-z@^X#p2KF%t~Pd?5@ z`dxaHhCAsgNIJ^Pckuj$w`b5)ynL9#<;Gt5tFF?C{})Q^qSaz!9tSe9O1thfyKWT zYl@@52YU`+I?DtNaeQqYIlpOzB0?@uzyuoV_H>=i5_t_GPwUI>IE~R($f2-Tz@f~= zQG;EFc|tRA{t;DT8uQggukAwI0~)9~367?fg%MG*L=Y|?{63FrHHsL|{4{ikFWz@d{L;H4RB64$u|{%(A{Q z$qB%E@x{Gg-FmQHFuzK~eg05{`+t%-?2Fkt?{Qf@HA$OHSME64ea+3c^2 zL(FxWBFr(YhDd^#w9gIMG>K5yG@0g6 zpQ^BqHZZ_<>ZwFmQAmXj(~t^~jQc{M8rK%OP{@9b`sKRY5~yzNF)EId_L)7DPc$NW zk2tc1<~5jOeK|i~Nflgn`fVAiGRdWG3#E*hj4-DS4%7iEa_1=6EQ6i!Xs&_mME>CC zPbGUzhq&yrN1HcC)*2yOqNc{M)1@K_LyfWzj;s~N!E+kojglKFV>tg332BT!Kp}xT zk-CwN@$6Z-(;|Qayzt>2h;6oqIb?hB$(W?xdRFhk#*f1E-+JnRd$uB|4z*y7R)8?^ z<)#9Gjx_^@F&nvS_Oi?g6dUZ3Mx1mYxhKB zC0BqTcZ;6!M<&!hn<}7o%%hQy{)qH>P0^m_n~s$p+-n|QIc^VGDQyp{g|lYd?)L8N z8Ae#(Y$fO)l(jX2V=MrFeZg5%VQ~Rn%M#ve1#s6Qwp~&~-X{<25IE=*&)XlkiFd7W zIar<&mXg{RjBSAGVHjN0^NQ4fwo3bQ`cKN^YLB8$jNn+jEQRh-S-FiEF>tg{*lIZc z@vSyWkl{@&swP!%`COkzjI)7vtp8=>8QiE7jQitXw$|*m(`ECrAKT?O@@6B24i;Lv`;a0WmQSyhG#ND(; zuat~feV0-Bxx^2_9m%4Z}laqJ#Gzn zQnK+lji?$41z!`f#SU-Sw+=xx7lY>+1E91`zx>9KVA~4$IT$1PMc)g7&*FHeehP-a z2P%bT|Mc0wN8lfA&KH-A`He8k=s71r(0r?YC=6>LO3?rAwtEFXQ(a;OIuca}9B*nM z0`FP;j$+*}8X4lYdk}qq&V$)tk^jm8Pzmcr=z@D+z zoL>IKqg-|q_O{Y+_+8=~wOLg-<_2Rz@OS5b{ zTj*nUd(}ka{sRa+A)Ann5w(B?jSP2JY}(bmvX(H?8c;=89X9R20ulC2=<0l&>s$Vs zApsY^>=cDI3793N@SO?>o=pYWN%_EAoWmyzXV@W}0beXyJnKq1&1$#)n`~?RfDEgx z&gOcB%;f>$6yxb$W3ljMo?V_=5lF%a9|s-z}mO2P1+h6uA1CODEbb*YG2mJLD6QZyeQegW*yMpb6laRy{& zaenezYKb8n%)fz$JNyA>j0-0IbCJ6Xu(aCrtP3B3a@KQm5;FROIbld?74yI(ruyte z)9|~gz_%XDQ3Nl%Co;S_mLux(dhi%g|!e`67M>AWgRBv>dmWC zZWBZb$5?JUXHb+n6_;|s-p{=;*zmYsTs`LV4N3(5L@r$3eLR3IwzTn@GYTzi+U21w z0rmAg`#yx{$*oNp4%^~4oK45Jx&n9)&2etkd?R=}eKOolUt^w_C8Ym;;6KD?HMqzp z63uo%ksvE-0}jI0_>R9yGFkC)?%z=?*zEF)-Xi@??D#(3&K+Ye>KQ5IYM@Pe8^X9t zaczE^v^wDfoN_N_=s)v4s`ubCM{qXtt*Q%tcG#QEdS6P=Q*4V%&2Wg0m(Xag`kDWz ze_GWV=)4623Zn>i^V;VT@Mu%AsAr3BCer3=6GEMgx02b;|M{hrer)^r6KjlE^b8>) z#24pE_3E&$$GdJ>&uUaRCCIQ_1k$BM&45wYBbc5b|*^62F5PqcL+}}nl z1^^_nOH@D!cw8UJ^{AL)FZ zP=v1q8u#0s^NnttNQesw=W(%-vp*RMp^4|bLVdg9u2b5m%Ozh~GFiDe#m?wyb` zj2Aw!syf1a!5E$4UoDrHeutw>U5t%_GpenR1Y)GsOyLf3h!vU9r!iU3~x_cI+KGffCxR@1^}4Jz(GeMik(eSNwlXjNHtu|1mL=fp9bb zw}J7;!uWqDF6I21sHN|)%Q>NaV&W94mGwOZ#`+2b!}hfHoK?Va2eleKwf_dRYn8ZE zlATw2C{8b9sFSs{i}hINrJlNV>r~c>y;%=4(!5kN78vJUl`Qo>+>M7`s_Khp`L$}= zJ?XuqAXI2Dfmcw_HftKU0wkBjM2x{vD|8Oz>eLXb{vM^!6pe`0=)|6t7NwJxbdOc) zPq`x7w2JsAs5NQJC;!Q|qM)&D&5X3haM_b$6o^n7XH+ODP}7G~tX4CiORh2PF_ZTs z$v|757k;v32iIt{z!i;t)QbyqH9<9xEP~QSIvwwp8qvl3*;rjJ0z(KK6@;ofY1?`7`c~MF)mkc zQ*SD*&r&g%>9)0RE~NE0wGFc!td6DGb z7sH(4CPNWB&%xwnP{3T64SPnHMxv(KKPqhIN+#b13iW|NQy6RD#J|1E=Wi^1>j{P{ zNY;O^aQJcXJqy#5wUudhv2tq^sHNcXND-Q`a3ruStrWsQturOJ+mlb>Sa&^o!hz&@ z__Sh-@38_^50x}n;6nr@K?@JW4y<5A|V`hyaj82aJ(EzMgY>5|~l} z6{D%YxM$l6MYxTHUVSkDtT-W&W7|`y8oDDHW+Z36*U+VpPOi;N(Z?HRXz*L zH3iNG;hYj?2z^@%X-S$IPDe`m2L?+W+7a4V6&oL=xpLnNC#KSSIWzM-AEtLDDgw%} zLTX%t4gLzK8N`{rN_Y;3&DqAW)5`*!deIz&4?>atVaUL>9vw*s`L_nGh`G?56A&c3 z^O};l!EZpdU3pMv=4`-To^dWuG0`bRGQ8obvVaA$r6F5+ZOmMLpt5yvaN-OqO}Zt@ zTTn-2jAuDJ(-_aLE$h$-Lmm4N#vACcXfTL|R_=%z`LytYVd`DF84ZczR8&Vy_Q!}E zt+yE3Lq@C(8gqg&Fky3nu$u={m9i+zI3$Je13p09PZ*Osx@IhZhcb^m33HC9C@W(& zKVLLM>EZ=v%I+r~Y#GE@`>2|IXy1yA)m%rTUTe1BB#L|fKDi`n=ljd!`*kQ@@Z-YL_^L5ch*oTP2F(EK|5kK>ECfmdyzht@u zz4X&%i13S?L&4zH>b*W{j0*=xnUXXdcO{=-*+mJF-|S%NPCeJ2oE5b*G?l)kKhD3Q zg0Xx?Bd^pO%Tf%tuX>4@yA^11gClVa&DM&HX_iV7L^PJ8d`he{`#qlU>*Pd`-JtAi zPP6idHe>gr8n@-hMz5;o4|A)y6z>wg9>8zIgd38Zv7wpC1&bq^Rh6t>i1j9istC3D zUqSi^lkF0i(W-5~Gc*{6MXm0RB?&HlPE)IV+*opgJjtOPCI+uArKhX0w5QEF83~{E zKQfUxI&TzWNckg9rqiSwI!feMlG0zkY5dno%jEPm1~`HCTk}vZ2gTRKgjCX1hk)=@ zdD8ya-#B}AzKhuQdBU!coD|8D;B{e9iu^p3W??zLV2{fJH>ETUWrXOR#)a+pxtHZD zvPy{WIE%ROF8*nZ-X;b5)eE%r$F#5~`0G5@Q?wZQVVhu-=7oY0mRTG!!OyapK z#M@02tdXzNlXN1w5(>|@yCTshj7;R6exs$cs5B$-kcvm=A24#&tnmOwRXD+E~^MrBJEzk}S zLTk4ULNh!2Nt}>Rb5g0MZ7nFF&K}ZG3034H#{`Nuo+yo)HC-4V2az z>99(N5OomR-9X0spU%VA)2^x9=|1 zPLy5r9&uxmp!{wnNN$C3*G#_oc=t?Fsxm2$y*x&96Oxa;!3-!jw}PNK+KbkhJ209a zY;Aa4Oebqnr1lY(dkFbn6Vm(xLLW2`QD&LSq(BJFqsSt>k2$Rt@w>4qbK*~?U%J)! zV7&;zaMUL%FN6TBlE{uyg+N>+J|Awb1+~9`ev^X`a-~A~#N?s~$Iq#HUTiRVYlMq! z&!Z{Wz$ggd$RL0LH75%J2~8ky;%G>fYrzpZ6Q0B2t8)=mQk zg*=w0Aj49BV0c(ejoac#vWlCShXv~tM+@+HpjZHNwBI)Eg#VT7;5Sj^;C6%?_R&4c z1{~%a{d?%QgWbg-(;+sg(Oz)utLtR&XxSv212Zuf(caI6jcw3%ess0(yg8RRP%b$A z8$|GfSO^DXic8#;ezl6uB8L@2pa6>PdeV8)H5K|*qHZyvNIuYmpuXS&ZT)DA9PEyC z0|)W6Dv`wc1LdvKo<gyPWkHRm-}VF38DX_&D9tzTsdw9;U3om5Sxcsei+dbP zKE!wr%S?2qC(ao1ifnrw4}IfUXcC5nD|`e!|Cs=;AYT4Tn!iOJ z488!u0m|sMNEh6d?wA3Vl1YH*D+&J517Bz;=>GINU`0D>tL^uKBto|{NU1vZMu;bC zDB{yBQMiGft(4u#l8J%n(s^mGt-;smBn#q&4cLgX>!Lc`a!RW+6Qefe&{B@`WAa+8 zdhRF^5JFqFPgjmB2CR!f47W2->}5I~Z^R!4MZ$3m~`dYZy} zr?CFWR(Ul7Pq&%O19-`cuw*3;RDaPHgwjYbf;PUZr>v?LsO+vmY(3<+P6)r}B4B>G z4fu8GLgYX5Th{U}IF9Y+^Ow3zS5IEC(Gf}2ifVUBpFY2?2+?s*9vP%-uYoIWtC+yH znc`cw#e>I$A_m8KC~f3?1E@dWA7mX@va9Kbb=DcZJTbVR(B;X5xTaYU{_O#Y)Ob|d zks24Mfx?aqk>idv5fGyXqn8dVzF!sXPXB~gl?BMH^Y&Q=PtdV0%wPa`W7} z>{4F-0B*Bmwd|EgY5I@A)Jk9(c2cuPh5Aid+u};j;*3_LPb{889T7pN>QQc51!9N* z8`+i7k#rk_6L{aTtTgvC64|z_2*a-;l*u`35oHN z6TLEs)WcNa%V0K}2qMxMz#mKwswD*$NU8`J$d^q7;noriFR{CIN2q8973y3Qqc|u1 zQzQN!!V7<-rYV%Z1XJ!2@1bi$4FCDL6NkvVG|DTf?4&=#0re4&vjYOe zk)*B0!Rt0wqc#xbfk1JFSQTV-Hf>9<5Mf9JvueX-Is4h;+@uf;5K-_>4m8=NymK^p z(wX{JgPoEa`L2hbLceJ?yMfUIUdj%HTT~69W%B##n&4r!s#B{g8#cj9(xaBrI`;zk zjK|1Np~x-R4ga{Ob=@m3(tUgK6(LUt=A7An!yNI_!3Q4wF6i8sG3K~u^N07G9Ud&- zZXyt<+wO?J_gAeRK&(2)eI?%Mqpd=-z@E3J=(zE`IhIU-WR(>R`5HWk|JuzlFfIia z9fouh7Yz71k%WfjH;cyY??w_d|eW=UtRNT&FS;H^dfI1YVZ|IZ}XqvpgDKSh8kh zkT53#)=WOGWPPB0w;5l2BuO0~kSsS5U*|Zm^|cdFChxCpis=)Yz%!ZYO~#pY-Zii2 z*YwAY!oC1S07yS@Fqmg>@1EJNVDJ0Ivlf1KUK{x?dHtw@+RTmNqnomahG(>I6%g)F zbcgEXR&rcv|FTV*n+B2VN{=pJm}XwZ?YeTK97njvUC{!3cfqGGAZz;_yx$$uf}mD0 zN%NybvTH}CL4}ddsX!LFWWb}pf>X^GXW~CFE(=gQ6@7qeS5$rdS#T--2W0;Pw0k#) z3QqC;e){K$!z=nn)k0wRJq8g%+^LltC7F}~NEJ4y&aM5=x@~-@X+MyJu@xF5sEEM* z40U{AnI(H3N{;tYAPtenZdjkDxf`1`Vn5ppBLAr1tB^5$Mf(6m)h4z)KL& z%W$`g&=WJun%}4KZ2{}s=Ozo}*Yh9Hb(iHaW=T{J(CTuN{Vy==-s&V5Dm>%hxqo%$ zT_ITV#^-ee0Ah5%*pmi}6W+=4`Emd8@@!uL*H)b=n~nQDGUlYXx~8~6+Yx55u#AvN z$LUHW1Vy=4m6vNx6U+nNYe4LZ-E$}Z8H+3-g996l&4MUl}i2DzOsmsLYBeSpWqUJuRg%XgItVAyNn>c&~%AgMCo&9{15im&8p3G7Gf= zclti584qv}uNT(*Gk}d$*6cu>=z0jk6YBg6y}<2>vHDv>_Tu!qzd5}fe}lP6_GfrxxjbIXkQ@6>B0+TJ*omMVabUPqPH7 ziX61c6b}OJI7tyx?2p#$I1~Gf!~|m(*PyhVYxaH6sYGTNf(bt^na2jsG)M1}u-3}} zoIPLPIp5zCP5nC}uL%6n(38R0GA(&wb$(POi7; zq?sQAwa*saC2r%d^&0&;gxE?srB(*G0^g5SXIm>6T|?-CNtfXdalIanpI^O?q{CPT zzBmQ#*JH*P>)Z^6GFTj_%~1RWGk4-qhOsjdcsk&=O%Py5T1ldQB&svLr?D%5h}`?% zvSWo&|EO9s(2sJ@KjTTsW+I=5rgI$xNGaV-U#4V?HJ~dj!2XT3%^d{#OD479nr^`y zi260ed{D@ zQf9S~VQfZA3Jx1SA69LX>F+ZJ&V53YpQ8{N@th|gPf{Izr8j**6Q7iv8JyH@@Sqj+ zDz$xQX&Dw;KsVj|!Vj8(EEm(A2+J3PJVAqItsurONOVX(+u5(UyxMU&nk)YM%+ZW5 zM~v^Lucpr?(!0eSSF;ka6d${<-=M^P5=v>n9N?&+9PG^h6*-#LS_?)9U}0zG{2xYL zt*L4JHu$X*YIALe5fp$JG}Ju^edlRq&7Sx=PGBGz{qEtXPu%g>WDfRU1Gr`bOD8G( zgz#clm%HX-a?CXH6^3MS5#r?TbS!WKnL;EhXb%`Kt99@X>G3+NEZV+(LV7)G*{8`H z9R4;2VPrro2K>7D2W7TwDnpBSvX8hzwAPi1{&1C2qWD{C0y+^elWeG(D&4iCN(()^ zI+QS1U{N#JM2z74B9&3N1`OGoU5!ejc+xzGKRddPX|Py5cba&UYjnV&8?%_0%LRm+ zGO`#A4%o^c!y=H~xE!_jvxaJGGpE2D5OsGT6;OaJ)m@L3-X=@Pyd#ROWlS6;R_9Z7 z|GbmojR-0fkgC`#dg>{Jfm6-fK36Y9fKP$pK;14o$h%M-`tGSh75TZLW`f%2Tne(w zp&~BI1)ud$A0G3<2wG{>0R@Cuzz_<@BMMUbb%>aDs#uAz^P4qN`gn+G1``>GB$1{g z{2c&I4AI7nSzLpKA3JD}olB#*8#w1NfP8ohE^<1PDj}LHky%S?&|+GQk_H;bCg3o( z^kq28RW*PX3SpUQk4P&xO-M|)RH(45R}ECQUn9Ym4p>)wj;(%x#yH_@1E)L*RSOnm z7QENG*cuo?5uYm#;>6rKq_hZ!ia2HP zH|DVl_B~2+Hi}~h$}eFNA^HNanFzd9;F3^!<*}mS9w{76F3Gdt0CXN0!ve^pI&9h; zaHfHd>`=T=HP8y$3~Vxxu`6ITV2OOo1(gmJY0#jm6b+$itQCkV^A99oE8(gzjbH!~ z$071bxOxc?ly-l6BD4l_NFp!?(om2@qbcZTbbmRqZ#fK(3e^@;UXmkIh|eEE&#(JC?DS1W2uOL!vL8�n z0viZ;r#K*+V0}_4+}s9=Yp5Za`6z&MMG^+VoFFiI1~9)5XF!vNrvwaGPDaQuML($| zi3Ph0yjIY+MX(4W#I&5gX$Uck7cr48l=K7vI>=_A3f$Qhw52Gj4=FKKl{^qrazA}$8|16+?AYxk- z1$1(b3`&&AJ+`|>sQ-KrMDRpAAu8+|g0h@KC9#6aNt>1Zgg_XF_7_;s_;2YCP3G0@a~pt)hd_}m(O27KP7DS}4-V$z$x zZ-XsS!3Hf+)`pZRYgB%{3n%R5HSG0U8FBpncoj4dI4s1+;(;64kiX5Ebs*53exBaF z-uhD*2PzUR;8GW7#;;R;Z9`sk^hKfN9(LrS71L@^Hpn^YmVcSPAq0?~Jfbi7XESLQ zv&L)g`0j1NQhX|;UEa(&5JgId2LqhZ55v<}$Op0}s%@PA!^hjBc5c4b8=K-W$+`WW z^L#Y@?tJ3s%dhQ9ae_a#Ev&`?%au0KWG9ZyqvS5@14@@VX4Fv4qL8{1MN^xoZvQt5 zi;B0}yCQk&{^IX`nKK}CO(LB$+CRK=I|H(kMXAxavKKlv&*-c@)E48j(!J$jWLpPf z+&>?S>OJtht>HfIb>zLhsk^kx&|{DO@4br|y0Z~pN{vVBg{fUnlOAQ2ZK{K`MhB1Q zWTJX&PF)iE#8;cQS9{9AtieZ&ycd{oC*LM&4W;d+IUH$o&6(x zIwJ#GOOt&>hh4pIy-Tvx;A z=l3(<1NCOGbCH^;V z3Z0yVV`W}rr9dY?*Oq}>qnrP_W9!^79=*i_w24a6z`JBFSD?6?iBE595{Dx=nc+@ zSD~I{xd3ad>S z0u$NYK=qXgAKROLux-hVn`Sn4M|Z=UXfM2Sz>{mGXe&7J<p`H?fXv%j%OVcK1EaC3Wl{+8GPj7IC`lc+54u z%3`LVJjlc!`~fIt_haQE+s10B5zt=t`sTasOVYp1y8EU@+B`e;NFgt(qmh zVwvOjG;^;u8$dOeJa@t5SYy9~^<p67vnfqcqR&aaoIt+9HuMYD*;)0%a5 zTkdRocm~KRdT@OfoXdbWm^gQ)Nq7n5yq_lx5xXxe{^o8P+Ve^_5BF&i+-eiL)VInE(5SGpANBsrw!G8v)=!*Fc>*;wmIjg?0GzH#i>hmu%?uSlXq6+|& zwl3h_#q8F{xqr7dvdb!=oDnek&LSTsy?fkp!JRsdIC(Ph0)kH?o$vycbD>}mN$o8OJwJ4#qmYPwn0$jNtpdM8f4-L zzi#_Z_por1W>7uNsfY)79vbxm!~n8B?~cT3_#W$CW**Fa3J}+>00pD+X4Vd!-+>3! zqpJGmZ?-NzF$Wp^wXv!`!u}pl){@RecI+9abJ(_}l6Kerm0TCf_#6wty}a)W!#=MG z$8vQm8IF4d|C%G;({yh`-_uI{-FJ*3dVw|%R;Ir}<1e6k{*SXi4Tb|829%ST?SCf| zad9%I-2uSx0L&c!BVR2=OFEIh0(tWS%?kv__(z+n%i47cXSsLwZ8n`_@{J95VycgT z8b4u;t&gWL-n^=GSSQi`qY=Mf!tZiMulUfb-G|5Jy6m=3Gn{df#!FNCmsq-LnTBx^ zW?(%a{!0fJ^RY3p!!iv!lFdLP_J{0xyc-VxmzVa{ebfYSMM7va!qUHUcwFGZ2w!nT z+kaImf1$7fbn#)96?D?)#p;PQU5>)^YPdQ2%fPUP@)s-jVsma)|Tx;zCKqSp` zXaW-0Pldz10tsGd73#zTrYjO|HG8iL;bDgUZKUtRZ~&ieIrhK+62##p=KEWog0M{H z>;;lnH$5Y8+tTo0JE6l@`|yt>fM!)AI%ie6;HvYrAzMs4iDY_;XN|*EL@eA+f+`{D z(7j>9e**clf1*tN+7}%B>Ptibeuha@V-Qc=8S4l`AF{Md)^QS|kG7D?+ry(S#&; zEYDM1h0?Z#(A$MU^R36Q%`}E{RznC2<5N)X^&5hCapaU?V1E`&RwTDTX7-Vrq6i#~ zH_lm5Yxs+?-Rbl2EA&l(Ybd(B&B>U810H(UL5=7U7X#&*}xFHQ}9FuF&o+l z-|JU&lD}ey{~(J+6RjFWEls+XGN@~7_K#WDF9P9d^i?9#S&Pg9fX$Y7X{-C|+?Guv z47WXACf+nVgCs3}W82S!-WQ~ubk!42=3eAyDc(jj<6LimzXyC1ig>92V7e+Mfe)3Y|qYq!D>Jy*S(l)rQ5WY=jqz2Bu z={brMC*AcEgMkeI>eLe51dt&6VEIX5!fa95qFMxYVH`MhkZ2q{6br#)g@aeRn`k2& z#JB7sW!x(%#C)ec)Xn!SUQr=Z)pvqACavSpjKE1Y2QbBNwpEXrQ2#)=Q9XwXDfE85 zZrpsoJug|qjIdJzefE97Kg@i893TRHpLAP;RB51q`4S2Oes@t49=3dCr_n*zPH-{y zO~coIn%0qb^d#wqzkquErQVL;TMlvc;eX2gZP8~d8vWfY?<3D}a|p5LPUMhzAL-W| z`8m|Fk)i*dq5mO=c#?BkGJ5zP?jmFoqM?K+ut}Pj^uBQA21=F>i9Fea=;#b6Ur=mScjRy2a&A?B0WK<89pt~Tty!!|G&q``jJX!!JiZXn z?Tv6#ym%4lw|j?lbE)0A5!h4MnY)ThfOz-y62Pd6yVfYr!t8bcc}A!F*uOOn`TS@G z2x+CD+$oS)i}VxTys+_dR|{euyS%(pk^S7oeZr`z18>!ln=-kk-*G{k?^zf275@~7 z9QEAU{&T=BC?ANppD^!?g1C!cBl*CiRfiQW@$7{ZpXk_3u+6IzUtps3Efl{?uq63z z19=IwM!)?qa%^$tZn=QQ(>7g)*|z=*z)UdxNP4tV5Q1*vQSA$U%e>3iGABT~`&SqxD z?fiAaY2f=zuii_|_MC9TF?Pk^`~Xx&JMZtF3-prPm`UoN3rG~HtP{-zHA@!+ann$Tn;*6z!0+{(k@Qp%W9px(%llvvk-Y}uiCe3~L(NM1+B=BF-b%@r z3_Vt4*wsZ+l`|Q3V1n;tXiBEy$J|=0Vi3afqQ|kv0d-vf9lhehEQFM%q8X6$MbuU) zYNCmplnj%}MyY}>YNbkMPF+cv(u2Y1}_r$&vMReSG0Rkg=j6GHs#`|OitX$lMH zq)k(L*0PvINJox1WYXDVXG7V6K3=|#GsN&xTwAP}_P%!Y17Kw%Nnc!!TYqZR%KtnAqU z9n?u30dI>7RV?`T9*3B;|JTZ%0Nn{l)l85`4+t(#P}odZ!Dn$B8tzmPWn!hYpKu2H zF#&YSMqy&(9YFih{~t0XC_ttrfhptA2M>a-j2OW%SjYgeLacHFlz5l;!e3Q#_FP*KiBtQaKU=D=`!0TATxH)Sb5zN^u^ z>s6Cwid|596{x2V4xtEMnKz&ftL)N-#S#!hKlEVI&lONTIzqudf^`ejTjU#qwk-WQ zXJ8VItV(2AxSl=A*c7eCQM!)5qJ|CQXezRZp!y~m4TGFrZlJg@CK(+uM$OTH#4c)Q zh^VR(;v~#ukfI5y3P_UxzhXUg{xcvgao1tM{=3lO-vSlWNK|>>8MBe4#y-J3j;19; z6#7EQvjt+8_0$BD3iwnZ5z$OQxRQPvIBFnv7#ZOmu-p-X6-2l+#TdE{t~t3Rhb79+ zZ){J35SKvMI(P~rL>_j$K7zXH+0CX-R)@fdr+k6{Q~VfFQNUSdYQlz<2D2dOm%|a( zljLQg2-v2C1d|ZDnkkzr8AnhMp%0nfo`M zo&PzS*3KDe!vRw57x*TK>VkeAq$(>~lbNudsK0x`EyoPMUNulDN_zs=scQH@{w~Kr z%e+mMk{u5U9-shMi)qqkLPEmsVai(tXQ8}_RDLTxR+64g+wyXxQy&950kSfm1CKzY zd9}na$s1bHgpBNmsv?trhB0Qyh=kAchU1OPLnkW3$suoyp(Z>(v!I!~^83l-Bmxgc zqD`|D*ofneKA^8gvK{Ie9T+{ZzNU*3rDFh9LnvNs3Gi=RN<+o7F}gC4&Y`V8vr5-E z+z!s>X35aFurGaL(mc-$(t88S`mB$Z*ud~%=qtoiK^HpkyA5T~gPC~J9kO@?$hFGb znLZ112_eB=X3KU3RA`l|+aBDjmx+4pY4{rO_4usjavZ(t+EfJiet&z+-R|K!d*N+Y zMQIvA003{FRI~)pR@g`hI1L}xjYkl)4|QJc!FKGK{le7RQy!~nJ?cJbev1*mLykqL zI}Z@NGq0ufJP9)E^)B@qis^cfYUR1@?UPtthpBBzv1?C>ePKMTq&qAOzgYQ*VsxR- z{nFf>rGI_`)nzcI=N)@w?P}S?xJl?72FY=i1aPv2E1xpI(4KA~o^sj`31B)8Jwaxx z&vA1qYWet^SsVF>erWFm*X!)&Z%g+q2#ogTvpz{&?#*d5mEdwo7s}fMpd?IW=RV7w z(S{bV;(k^3oQ;>n@cn_Ql~5l*YNPGY&FnNi5Ou$8FE=dn1RFl&l2HIa)iTRWzg6C( z0`}vR9MM!0O$K`N%~oX?P6)iJPAFJ?$2#oT8SCGr1xM4r(e-9#)!bj~$@R1t4vo0} zj140-B;_tH-4#yVrPg-`Fw^V9y^A9P!j@Xz5u1|kkYbefk*3^ENx29*8y`C8EXwOC zT0=LXmM@qgB(-33dAJKS^+f+7HEzSQC|+i*}AFm74{vaerK zZIBafFj?NluLQ5^JYIUT3C~$do$*zDa8&sK&z?|v5|0o$Hp|cih`R=MbGbBjJ}Fz5 z#M(Y70(vY_nP^dJp=R%I5Ltiv&JliR6UuSUy;ij$@)oPJjTX3F0OWXvrh0JT0A8|z z`Rm-pxHNlhdt;*0G`W29@XHG5{+Q`4qV75wzK2tg^JZ5dhXSm_Mm`y%)>GICsPJ0? zh0R9~YTbF8GiC@)xkh78jF4{^DXAWA@SY=6Je=*UIE#&~(j%N?5O;GD0S~x5Iau`t zgyc>>NdsvX86889AHr2R>si-6|9S>9KHu3)ODQr>l@q)Dd%J6D><^sp!9Ok?T!P2f zhkICm$QS#hs}9_9tfE<3eBvQ*njC^lWn0~qJv#CNI`Wuoy+(2e<%vFwu28AdH$u$E zATj=8nD&fbjnno;qjaCr3d3>Lw4P@;*EZ3^sJnmlwyRD)ydJ=HnJRokoba@`^^@~rn3KO9f%eY&iMCigM_bx2YYg0Gs4r`CrIYo# z0*aT10-f8{NY#O3svqaC6EU>Rne;lqN*8*nlIP-HU+8Z<0ForM5>!`^sFP*5gQdo4 zG>%e-y1>m`=#DUBU>s;n=&$rJ%Lip}JsMRCk}B_kv5QhnFy%_7sgUG34&Aa{Z`k@} z#3a;jAkxx8DNC6g)aq z$n0=CjfxmT7cj2$ZFl!J15fpaQ$-f}0yl^RLy`b6=*o*0L$3voEE)pfhjFqbl@XD1BQSK5-72Es<&?{EZp>nAU z_rv3A2filrLf#436LcO7;eLQOJ(W2GOCUVNn$E;Hmpa2&c? zV#F6z(wHK0awSBcV47(0rM1c+J2>i$t;CcybfFDg?$tUc8IIQM4L1)vGr4j9brlpT zDLAVexMCM<=fcGtqV2EnDf>Ik~qrZy1k9AzxSZDCE+D?;MomIxGPH9=jtA*)= zgD$i1qFhj8j`_n!V^eHJT&NTO8r~^HWv2<2ih8~sQRsO6D0J{Av=1Hpl%a(&Sdy%_WYj4 zTM>E>MVDQPY6TXRSU5cvRD05w9n#2;wD5A$IrR=RUn@dQPbv>i7-uiv(pDVZ)n7hS zFw9kly$=C+d_EDle$W(Mp!BERZdg*YdWGj&Q){!R=OaAdAj6<%DK72zxa@An=3G{U ztKFISkKJx?%~n&_TZ{{QX{jF2@^-jJ_h~;bkNv%V&uhS!@Hfh4pg{k<>^Dem=q9@F z3vz9U(&12dAf8Y7{t$geGUE?-*dDHrbDJMw#|Hp)DV%wH_Y1diN$C*%rHyh^1J-n6 zm(cqne96(8(RF{Vc(}teMb3$FilBCgwX;p_SUaO+%Q zuRNgVq?KgA+eHjeThov<8u(Tw;l=*ffl~Z(M!jTQM@KfMZjjH4k9pf!Y8djqtSIFt zUV!2RH$W?G`v^aauO!?pFLI(&90QL*{=B|sy`0cW#BOUn%j1GX=EAEul+Gg%_oREA z=&3fEy<6+pPWn2Zm{Qr$QZH;Z-$2(RTMK~L6VDb~{l4uF>{RxAu+mOo#p{`chh5*c zM-SefZ&rK4FMRECx{Y{K+jxZV?OAbkv#EUNP^gpmh0wcupcV3gzvZH~x$_#9rDHWd z`Ub?v@Doe?Jy~|e<+X|OZ9SB*@@=X%4E|b%Ew~N0-_}z4whg}@Q?j4_#kt>GPj%mdZqn$-?|1rvjemp`iVf*^e zx5@5``&Mw#uFzXX;zwIX?ZRJmA>dL_mfB~}39uMNJSwb~>znuRvarm!bw9## za=HvWXtLTe9aNc}%C>9zUcS9p2y5|Ik)KrPwuOIU(#p)7T8}?;6r$?+GOW*zJ{Ywe zxMiN_?LGGqx-m;3IO>M2c{vAg>to-zIv47+-FqXf&KT?$lb?vNT!{hqPy$X!9ItAO zM)0#cs!Bg%ro_i|IzLz%3IkscLeW7VSBs@pS#7yir`4A@+lNcIxfFSGYZ7yJj(7;K zc9`=P;k+}kUEr&IjV&lMeuv)D71z|LM?&6notPT-jG}DRY|Yf7IP)-PNs*~m{7D9H!)6f}7{Ca$(ndnI9fSlQ=CqFowM_~IG{)N< zgxstBYQJ%If5~7ph@f}0t~Q?Q{rS4f9L$uBBH^4jUNG%7{|T!M4w9I|lIRy{4J#I^%JPM-OT@}wub*kCS+!E~6Jb5gfMfsyFf%3+#l4M& zlVKK#FeVzB2uvvss~%aByNzZ7ZDrbA+LU7_qC#3DiwLnZ5I|4CDhhUVOD~m{Na&bz{s5oBqO7y@f{ED)&;%u_FxWXC z*pp=o5aN{%2x6Th(hv!)tVYNb=)!(OrIkVOgcqvyc>OYjdu1g8hEOrBe|5j%AAd!I zIw4r2Gjw3i6#-PT6a1C>i(7#gPevfIBQL+i{sW2-cwW8w=uR$PAh3z^zH3q04?0+K z)&O^4{mjU__rM#K}+ zjMeE-bxA|3lhnZ+W%Vk2QC8BFt6oMcDgR>PYUun#=YUXqkrBlhv~}p}PyZt~a;oVt zYW}75FCA-J#E@h))L-z?Hki}3L&Xf48NOUP5ma3|dG4)uP6g`-rO+87-8m04wCn)J*xUS>t0@ww-O%ujy()D`bjb(k9yNw{U zct3IVUxF1105+1TYhrnc*WS3A1c|G%s^!ZiXNkDVcR}Vhd0>p^3QY9pJ#ywo zO6Ua4>%7rDd0azCV{RfJbCpnA)aedZ~|fP*=r+mY+?K(u%%1|lj79a^Tbq+fuqUFdm=^D!+lrG(d3|U zsR^fb{R8G4SbQ1+GjZ)CQ`a*dx2(AZlwc~V5NJpiZ3I(2t&;>FSZ`WG4tjw z0Gqkbc|4LJ!7IXLj`m2VCD^Ll8aFOjs&R+9E*4sp2pej2k~G{XUaZ3!P5jjlfPA}R5C0~@UO%^kvB_5hB?Gm}NjNOfApJcr z(bPa0~gq&0lW4+tjF^Xl`Lg=2LplKHdv0)yTHq60Hu%@ z_^@&XIemFjFTcWJmKFSc(y3Mz&tCR9Rl%M00))ClzV%-|&FG3O{Sy|5AR~VszitLf z1^Ie?-^chX&Z-5!MxMVO=38@f1-tmSx1xljZBE*~ag0E|P`-XKyzAYlw|(Rezcl*V z8SM@$0Ae4%F6RY5-%9laKX>hVf0VrA%g5ubxxSw!AhO?^v4XyD`^Vckz22E#da}p8 zeBa0G8~C`&q5(MCeD$>5Q_IMli8^J2p9^L8_Zr>4>9y&52gmLAjieE$VS9pKPaoIq zjg+Uo2}dG{X5qWOgZxGGta8)oB{ZWPEfcj(EDEOBGO(Mj?WveKxZe}00NmRHalYs# zzfGdS^on1wvPy*)if8iLy;CacR~XX!G71)?iQyl^xxVjjHi-{{fUgdzw|T*jH%z7- z4tZ;(w;Rf=xE9;*7L98&8$wk~hkYt3-aijBeEGfvj*X+)p6AaIYwp7IK7*Lg3o_l! z?~9%vVU9cnU7E6r-bJtifDFu)U<69Km0XXLBWRm0f%^?D0rpsptI6pocPG-Vf<1b@ zn19%$kFc0{6<%4{ngO)V;gl_z&(siHx^C21vcq^`4`U31Q#XRuwi$Ol zd3Q&h2M5?MdbQgL*aWEalBStlOg2k_KU2o`HPFlNax~dN@ zR{`qV`916Q1#?SuVa_cxOpRkfrai@r{F%1fWLpzXRCY!#v26yfTF2%#p~rZF-whYd z8XWx15)16x&SHD-fMh|z>HFGoR#-s%AdcCIB7!r?;vO$%JNXBA30dPDG@7%j85VX( zszy_iE-VK9wkcgh=@RQ*G=UN%wkglAQdBgT2**OIR1Z0>UrNcGZT9`4O>PFyj3~&H zrj_?x*yo5Zoa5eW?~TGe_kd|E07>tD-bM*Bd0(f3^6d>sa*0nVrCfhu1vrxw?Pv*z42-zQ&)ht4j8ZkJeqX6w7;j!O06sR&uqHCNS|EXgpg z3RF3&*13kty(^EzpLbx*1L8IW-`7fyVM#LlDR7fd01cG8TVH1xt5we>XGbL-*mN{y z`IRYz4|{#A3GBCA4NGrR9RG1y-j@Q!NSZnXjJJmEltcdB7Or!sp^8WsKiWi5M zWOYcqi+k`t*y*eLU$wun2<;lgGDB;P-TnWT%P40d$ee!``A1AZL{xS}>f6~s=rl-4 zSaa}O0=DHi(=lGnoA-$W*5_ysG6=wsfxFt)`wD7Kz*p8fnVH zt^yz0ivr6!r|kk}8A?@e|(g4Xn5%`%dln+y*`wGsDu zIQIUwFcY82G>g~2vm+>yzJ-V}J?oZ0ly$mxet_OaPkQ{qBIN6`{Z?0I0jr}zOuKqO z%+_i1ElEL+tjW zy^3}TRz9sbuRpCh;%>>`eYc81NH`gFU8m}6&Ein%7_dT?cO*L)3KzyHU5Q%Tp3?Ph zUV#$id$)_7ZeixW2~bPn#34XQE##Z%QiIj3}K`z%zUG4w5QqK$I}gp3NdHnK_r z|7G*}$1zFilkAYX}~a{NReZ`e%tq7Ulz-B>jgsw@9<9#7TmhOv(4 zMyC6(Yfj`Uf(!FU_Q?GtZvvoFB@ob8<~4sB_6VH-{fqOMb#=8EVp@>PeskN016COj4 zSp#QIjW7I^Q@v+|DT4{bYWZ$J=uh?!{uCm9O?@#D!OK7$JtEGj2lL3Ua$gmbnD=s~Sy$7~C%OGc%Jgz7 zGe@mC180#Y_6aJi2jXy>v%}ZIH_O2fsWx2%d5?Q(8TefV+b@beg(#JPwAg7=*B(}o zJH&eT1duZ^c3SB`K1#<7kTZ4LTe{P*VQT%j=%6Kh0drNQt+_ig%R|>F2lhVlvyz9e z7QWik3E`fn!v=zw&IhJ)arJ^s!~ExP@}A1yYc$6zOTOy}IOMg{a^n#d5!b&cVL!2V zdICRVC^R;T8>H|%QwG}PwV+pAKKEFoxQ zZV?~R_&icddWZRHO1>@2nQme386E&R;&Bk`_EGr_Ei^oW&rdgNZS$j*zK>-yi1iN9 z_3Fib4LRd6UgVl~*5-EU$?x~<8B_6Ij`657&eI5(pa*!M>pFauDE-=oJUEl zw3@m5`4tladS?tSCgqy1o!Uo5o~51W7p0%I)|I~Nc!3?#p2vnn)#b*eGrN^PpBey> zdxHdp+hJBpv+-paSNHHr^DucJH~-sz zj>cE2orZ;07a>aR`DZbQj^ps?#u!fdX$!<%%fm!~<>zD(==0n6O>W2PnL=(dJ&_)3 z5NA8gr&JzwdKv>7=fp`jj?SwDz3D#h^u~;g^m`{eC$})bgzk6Ggr-^ceai&weHPM@ z;@|Df$`NnfAo^4pDC@VgdLH||DvMYyx|(8L!HY+PvfdU zpxAE4aODE?46((EV^16o66b`L_xzu^jLgN9ql$lQL)PY@lKKGQYz<>VBc!EKjUGAb z6iHS}eerix)OGHeF>i`U)EDE0F}c&Hcxoh%hC4$hC|r%)3l;C&@m2mFQvfyo;9{8{Bo_$E zkv-zx5S{?BJiCc^4-#9FVoS}*mn+e~?RI_8Y~qp`mYi;I&zzO$iBMpRqt@`waPY4I z*@|o#MZ4j)Kas`U4xHlCxC4@LE)b1CLdZC`PvLJc(#QgsL_Xn7H9QfK#(6K9SaEyX z1(voWY!~v0ErZ`tz7dZ4PNBoRu%&i0Bs}VjwG?}GPkG*Ojc1Age|yNdfw46`<=}gk~e4zmY>Ga%A(b%)|O|A@f)gwUV7Ac-R62Z9BK zCqcF(ZYsyo7_R;oX13S78O|K{NN%&@Dk&C@V8S7E2q7gx?pR@$J*s1jiSjaK6o|tC zWE#L=o+Xt83Adq~QAuI=i`qIKUU*H%gcpgZI~&;{0-nJe%OJTW=(pLPzc<^gkyDNz zOFu60@m|muVv?Jb1RF9p`|7_@9k^LC{PM&2#_hDjSkzomd2ab+qX92ql3dcd(ALMO?nf$Fx(2m#*D_qE$!PcOHRw{M4pZx8{;?%vn+U9XtF|6cpYNG_(*Al8M|KcJe)|o^{3ZD=Vn)8`k-LH|v~kVwizA`}8~D3>&8F{B z`X+fYHX*4&$5KQ#wYtJw^C6XXsN!>!Fvq9yj{IJkE8_$0moLk$p$>qvs*^*wIGjk> zSiV6WW%M8!yG8x4>aZ4h@w{3LYMhaQNrEPr$xA-F)NVF;KKtn(6gB=um_6-~^}vC; zj8Vz^aHNW(F_h)^6ueC2;eRMiIVm5RxL>;!#Vj;<%SPvv<^8PFsh`>U%v}|JB=NVYI+`rjX`=7W;s~i zaIvxHdDh2X4&1i$1OL{$yltO@7lUl;ouV!^*G47JgQGi6oYMmKaZ2b7em!L>T$)i- zKVYi8IYocmp+EqS#e8J)#9NryQA)OAl%rr5! ziu6gB;eTs{MBk)VSP7bPSLTIGS86nbj(??Ukl=T7C)L)y4}5y0PZjw6ThxQ0GX;3) zwzzRma|*bIk}Id)yK_GU#RSh8GaA8mKFGMM%#cQGxTFGdf2Za;gO0OVN0{4U0Ck5| zvQVoFHE8)e6B5=q3rtGn*1WfT6sm?|mj_hkt0S=i4sNDw?Ki&Ki#2M;s=;2F542K~l5;rA zHu;g9jsuN{G>B$}elGW1Ijo)5e{-AM3B1i8Ip_eMwbjigP;Adqn?uLGxctxBR4*om zVk=9X(t(1L_2n${&9zqBR83XjluHwg^g@=}26=C?fSLV~Pa-GU66|ozPcoY@?yy6+ z>Q0V%W#u|6cJ{4%e^nyYg=(p;WN1fp=$V6SGS;m4wi(%hV$B6?pF%WR>pKYG>7KpN zmPi1-7pU)^MB!)4klleHVoqdvq?JAX`j&caE(ga%v~L#BS8c(J8*Bv5*`!*Gtuh(< zM=0ED*I%8{T?|J$`geYe=1%g8hGq8(JHACI1^C9_8?FPBa6QGxwv+SnmO5Lw^>nZW z=H0rI$`q!nk37mEmv#O46zz12jM}pEjckA`@rDdH(a^Xn*VG-$gshXXn)jX0i~#Ox zfyD>n=-ypivD)eI$AAkvo4;YQp!{>ehN|sUg1bR>$VFz1g4ORzd~Vc&RR?xNDbSpE zT1t$9w;56=gSRA2N22Pr>&M;Stf@e?=>|LL!q)BK-Ptz_xjLhE>UW%qT6?|aP5ah9%0E|}`qUpSYjrO)_aSoIYYdtz zRLeIpPYx|D(eXGPgGW<7txCPT*u}ROdar$4=R&V?na8x6ftMMZrafQhuK3;S{kHgF z;@0>Bbz9{gfqe?s-RVm$;RQcS5?cXJZ_^)DwMK-iX5L!SBIcP;R)BEW^t{zva3@k_ z9MWK?SQk|KAoy%H3B9&# zBIu)2j9aDwz7Ae!HMdzSbiYc=p#2j3%Vnj4ScEPiP~E>abyS!!1uwb(ES&)lhgV&S zq*n<(tcJaw!hz<0?Vvzy_Qlj{*M@MbrKuXGYsz(V!QwQX&@ZX*s)DStsjYt^!ybG~ z6iMm&$D2O)$z+Z$BVKv67A90^H9KauR=JTb1la$SOgY_A24b)2BaozB(xqvoAvzGp zT?Jqt**w-Y=3zVUnv!Sg;{*ZLc00!0_yyuD+TI5g#}hs7{MNJeP6T$@sD=q}E!n;m zETde;?|v7GX*Kj=eekXFX9u+j_m;7+{?>#qh8WZ6&KGLSdl$uHG*7XqEG|8bKy>pq z=_JKepC`~eLF6^%GOn#TP1i6}EEL4tS*v+cwz9Ykty8S9sY_zVm2L)LmI%0sSU!11 zDG*HExal%xRCWpz|+opOu)CfAKN*gS+)hMx=niW7(ruCYl--?&z(`#{H z3_=OuY;RgFAyP%GEy&i@ygAHZ#R{{KM87x;qP^Wn6#41Z+ct3HSMHA26NW-9Qs{fv z2|HUUymsXvoOqC%=^w(^JbffczOx~~ADXEev}GcWQckqv4!F-Yd9IiqL8p*(v5T5a zE8Yjlg7jls{s90zSl46l7q3UhfK+8|k~t|Wg9NFWHf5}To5hd~z8m<}+dY_gR{Tq$ z7xt$>kJU_ABd&FZYizfmKHI;1yVbq>mAD776Hff3qkNG<0g?%2<^U+nzt+-tT3^y#7K_j1BF!w~ms7i)mCKUjaRZs^h* zXiIRCb{+u`$~DntRx(|m7`9GZ5A{lyHtLsJ;y&}Vx2Azg-y0@`1RcpMb@%H;cUt^_ zI%~c)+rLyNF$vzsYEexuw&B`XMa>?)r zsx2@o7GsqGn<|>-m2)M^#qq_`@8Zc2VKnrhsu;T!3zVum1vE04s(x)W_NwSNFEW(^ zHNgPfGuo23!_8a)z}MZS)hmClVdrykP=z6l>W5HrQK58NC5l)@TRzQHzIXf}rqmc{ zGUIb{;Y5XZ>7QUnJ*wyb@1THqc$%*W9V|_!VwjHto2*ebC_;`wQ?E!Zh@1akSMI!^ zD?Z?YrGuqkv0+qw1BHr3iUHopddaoLZaBw*2Dmtk+eE22wVC=S-Y=I3PVZcR#c(|BFkTZ@y z=x--6O9ZJ&=5lT-c8TJnx&^${3{YiWj45XR;Rn3={g*jB{R~oBr!b2c=1Jia?lHE| z^dqi$JoB~XuHgs1`Td#sUxT_9GcwB zybi71S2YyWQeog`@&JGb4GDR2lrd~aDm(>QzW_8KIge?1VarU6R`Nyv#pl>7D+3%7Wn%jEJ(5K|Uzz5X|W1DRb*u+i5lh#ip?5 z5>swj*kiJBN8)N;nBBD#dDv6p~>EEtNrqRvo zgEqOQ`&?419$b&uLlO23g#p%CTNFocaM>Jv3Q4Dt%tiGM^KymG${~{>yY;)l#)mDE zXCa6Ea^t_|x{G|;B}GpRv^OZR8NRTla0DNG5ZA?gt^w~BSzaG(+2reqⅈ*^8(?U z56NIiEK$4Bh}-*^UHwd52}~}Be>eEYSs{nYW5=PHIGpr9WJLydk+3qknS4o}z@iQr z@uU-+kVO?7NetoM$v$cpO*r<Xg(|8iTe4ST%Gout5MT zTJ%!BgjQrL5L(ovxZJoWmF{VbM~M#ZmP_{bh4q#owv<$Na5K(*h4RYlXHd9#0rNQt zg*%WKrl1?9;G5||PpI4sJ@-GaahSS_&(f~%fz^l($Jl3EvX^9~nlB2=s8X4_JyPxV zfMJ1fHe%p)vaEL5A$nt~zqOp3XnB;IrQ{GN{mxGhS`ZgZU>7?E@5KE23X=+}bRVKJ zuujXE+HVBkJK$LM^8}WO0y^#Z*!bcVxn9zQYQ9S-k0 znqjF?UeKsf(rdY5|eC%hlvKhwugflGmKafq zvmv=4La3q6KknT0+5B;5CEz8=$jP`7`uody`d5nac)b*tDx~y;Im7mYGhp*9J}wDW zIt?|+T$D_8P=6O{z9$`ZD&Jq28a*il%Cg03r?VhbAo% z#l$6wEJGd-WWhZQA%*t~ia2cGp95C1AF+YO5Re#5nFb@&gTtwVz6UUfG}b8CFa;hX zHeCsp3Y%n6e!!$Yj_QM8#we5nzMFmEY$Sc)??7>w%K3F`pWKG*v0t~nzb0mqT*DpRd z<vKT<<>yF(ybc6=@Qh`9=|LyLQmRacm7JwVq1F;d~@a0My|MAUW;sABQ zL%XhHB+M3jPmIINAM$XgUxA`m!B%uQ-aR^YToT(Ppc0)yJ7GD%qP~nF=*38IMGoVq z%QdPRwU|$oIB=-e10MF4=Fn;1ce~sK3mOTxAoE*7^EiM-YO{ktD@H z!kfWRJiTVnLCVSfIW+ayE{Ao{Lbr$CvO-q*Y$MK%KMA@ceL2przAikWRuzcm{p0w- z5-h!-+KUx_y$0$UFGO04aab5;>X^CzYE_GJ))`ngf6=Zu0mRVYh>dE9KdbPhC}B0L zVQn%-okJCN^p8C5szp{4v-if1p~LddSF6N)u z+Vp*Mfz?HCVK{@W~bEq?VaPz%$v32 z?JoMGdze}sjU}`9`59DVsPS}EU+F6Q14f5yQ#fa`-Cg#TG3l#;@Wk0eSw1#|iwn^j zz^sdw1L(nQR7&?rOHFN!9Ce4W9vZsuD|KWE^{BP&op6Flc3U0YC6+-avUsw*0S5tN zCH5Mf^R|YL@8cbw>+KrAL&OLiy}&WbVLoX%_ccIma!+!~#2w*58x){|&Htb;S`J2Y ziA72q{gcpvHrdS4Gp@H2zy;RfoxTPy+qe6N3^0QO&4`zvSReCguZNYag=KmRgj6c* zW}ry`JYbGQMBJ~ck?N%wM-D&Goc(0AZ$T2F%`|pLMp83eHl{A7$B=b|OiAk8RV#xh zVNoU;=A4Bd^^-gIw`lCMqhrYh*nsvKV`lUn z25#X1an4_*fiMjA<8^~BPJV**y)Fc6nfuwN92&WO%1^R=QqKL~a#HhOYOXUHJd^iB zdOxc^a-`Ngus56Ovk#9K7u;24R&k-xOu zAV#I$_%9Q66+~d$ScWO<-ozN*mPOldt?N@Wm2!4E^ixldIOfAz_EZ(2i8&vv(r7eU z)(RNd*u2poY9~L)xPxFm0!Z#!LFsFF)92BlzHjlIs5i5ev%q`T{mVxXvM?wv4G{2= z;*0QQa~F~;D)d~F{;>h+zTLBwn@DJfyue%2vHDAk0ef>+i?wAWB@koo!0MBJLaRdu zc)X-yYjk)vW|P8Qae0wkrdkg~uDQ=H@5=R0Pa#rI6$5kd`K*E#^}{zc6uSITrV;Y= zHFhfBqt|f9xW{d}R7qwWtMN@yJ- z;maUE88>^Nu$Rdo;4mMGJC>E3%qJLjnnm0t<~ES|%nKV9;c#=kRF8s3gU6-EkCZd# z5JR9)fVa=^uj`@PF1MGP_v@TF3m?!~IX2&rF^QNnNkNMDb!FQ_1?bI^t2O;9 z1MufCR&;h3&R!_D=jx~xHsC1KK2M3;^AIpzJsA{dW=g$+BaWlp`3?$F9}if1gF-Ic zK;YfKC&mV8W%$4`uB_WNtvNLmz9P*={qzD1KmAyloWiXe?P8Tb7^qJ?D4M@4w%F&v~AE?(>{;@BQ3+?sLxPUfcV5$guQSSw4Hk zAiW||gbiK_?=3NxU2=t=UCO=~9KybVnUslNTz*@&J?xCJ=bn%nAJ+;)%5|AJKYQ16 z{Fsh(6i73CTil7%NQ!uo5P7jhI_$mu!^bz-M@I%((= zAUDwdl6cuWRk>lVxQ)Ca!WsJbl{yzUCOrIGthRSL*~4##w;GcD&9Fq&Fn9gtKPWIy zA%#A?^M#znNg;3`Lh|-Cow@w9qrgo4!)^g+#f*af?;ibc1pv{pjXZ#; z<6+*}h{p?`az{_!KP|cw-EwYka`2pPM8wWgrKnsS_`>Ro=ZQm4`o|oQ zEW6KZ>N|*-yWLH_;_3Z?ju&??vuLh!NqM=!?sr3u9gca2KBzUQa8{WMlIt(ceH*(8A8r3ukM4KokRuJ_*2h@5|PC8~4vgV)yT^_xEc-veph);G#ZB))!VR5bPq(^pEB zl;39D4VH9mjEKj!VeqF2qVpq~89X$yPl+DPtB_qv~?=$@g_ zHbuD4vjR7pF!gVQe%q2f#Dk^iqmh9&!N@E#8b43go?{#yxv!u~G} z2XxrO`UWP8wp4HRmx-bJ1In2v@#6bU_{8`@>MkPA7dvi4T)S>1AB@*Ex3;n2YuX=Q zT~p)RIkNN3vCpxux33og4J>-=`L@Ww5={ZG8?>u6hR%k#kOEhS!sg2dhLYGYV7In? zX-&5t^5I6MW)tw^1x#7AAXT*4&hitC4U>~A=nJ6Zbgitte0J8c7*@@rI7I=+88gw) z9?@;cUrd)16?73$Ow*v4vHCInrN~tEl-HW~o2Y|_KdZ~uV)w6p${jqUP^z=qbS9D3 zKwG;1k)-#yTR*c#ewEG#O5n#Gp}D}7@|0*WTwX* za$;8?@`wESF0`0dfezsWk5626c-)Tro7!@tN_t>iwCZ>QPL4`HTqUM6;eNhJl_t10 zgsy*%28z3D4_C59M&fGybrzcHt3=n>eDEi+i|XJeaU`@pyT?C*rMsOU z;qo(vf+Ev{ki9T|;A5L^>Ma^-TPHh5xkrc41RFA7Zk^h2U~?pfIO1*LNgNl7!s-Xy zveR&5b@SK^v(7VT@=Ugq!hlYgoZtIL!j_sq*J|TnV_lmQH>}Yu=etq%n39@CSKo8( zYU2+K*z-!CfQ&njaJjp~N^6&HN*CNCUpa@u=UktNU~erK&m;4S6sBL{B6U7Z^>Mt2 zD=WY-M=UOhM5k?#8ngi;Dz;F7G^vT**ZAwr^j7C_WNp`sJ@(Wn(^rmFOlzqqLjj1m zq#8+@q8qdBfrGgt8S+z58$dPCU(B(zLrn9@OV9?0`{mU6hBviiFCGeHYo*AlyZ1E? zzs}^S{|Zq|Qg^4Xp?@bWh+&gBgPc8O)5o!0fI&R+sl3ixjXouLlojY&(1;T05Sn-t7=Xa*lKT>UG)VzoK%>p{YBYW__QC>t6z#8;8--OH_XlxajFyIRtG$IcCVDIw08EqVFlF|wieV-4B|VvLnpt+D z(oZvlKpD})@5%hsx5EYC1hzli^LAFkQC#X$j-B6bkj5^P75o+WfST=SDWUp2TU5E1 z-MZ1oBu7XSph%i@_~jryp5{AKxlB!p%;IUL_sheZ^>_6>Fnc60$5HXyT#-lOvaCnUX(#DusJvOZRI;o`rWYG|#2Tt=pBT|5gvA{_& zST+dM%0_<%qKN-Hb6eK}aLN&d5Vb<_iPfpVx=H9dY zAXH0*|m!QySw7p(+eL*2jrf@9Z4eRF^RL#=5vI)rjkmStc^Tzl{Omyjnq> zwEeCZ%u+L~&0P!OZtuep9utyyX5>Nj4zB_`}#; z$WITMQ5d@tA#SmBEWM{Mcf{gqQo)k&29hfTPv|X;Ux?(!u7jXAW%H=5q2Y0UGvF3u z#ZDOi-p<=xVjvC)ej66Ed63+xTX!k)nI`p#uNH0muNwLy3@8&yz$t79a>YmDnK)%f zd7f`q%GtWfL(6Fl;<^yvL`GuI<~D0AR}RKVOMNZGEGjTbU%d$VVOcdQkIC)_Jtzt` z3O<1BrOJKKhpjIAF#Z3u>ZZQgL)BMF8!HTqzs$mk#*6ib*2a+)g-bX8IFa2Hv|qW4 zWE2`qNN&ZC&D?g;7m;QVcF*yIEt|9$eXG*9m8IgyxZ@j#xw-&M^bm+I>3cn@xbzY^ z-b`@*0cKC9{zCIM-j6($9<&5zVeKAo(o_l!CMA=M5Ac5e^QC+8be&-&Y4ZZ|6P;4j z!Oy6!$iljd@$8p9xx6Yt=f> zAM5-Z`kr!`(kU16kw3$@7`wPWjw%CP3U+eUbaVuc8l;9hx9hJTllDQ{#JZppn95c7k*^B)QSS`_Sx=8mA?hxQ4twTr&BuAuwMw%_@~T;v&Rqr@5mkrr45{1LT+ z><1SuV4ts_^NLQsROrZD#6cv(Oj?Kmk0-w(FFTdGnSNTFVW-&WKf>) z`ls3fS6QWTjT*fvk4f1aH28{Rl#0Iu^;J>Cn*saSZ2vK|u7dwCz zXUWROR&(0Cr;$k7G-b~XHV=EoY+XXe*az`V)K%VL9DEvYjJhKY6k@$WH`Q`Im8<;?# zzpSI21Xr6Kwsn#E3$WMxs3F{X0Z+G%bqQj4biZAQhQR(h(9>xsr_26A5Wc$*ni~%1 zNz^?ETH<2~GSKhSBV^2|wLA4yq*AMa%}TuTq~dZWOojl>w0xPIU>!81GGq0szhm*- z=qb|WVkt*xzq2X}o^dYtijE z35?z4hG+ID2;gf%q)Jzo2juI12d}0!luIxO-YX}-lWCqUb|@CV-(rTs7h9X;W^dU9 z_D`x=G0C!}4ffoD&e38($bXK_%NLVmo36h5lz=a%2FOA8&0b)OId*#Vlijnk`o<;# z?qX8@UKY(BjqOW5OWDUsSs||@H})M=5EWtph=Jn zFKjD{)J7PM_yItR@UNyG==&?TnIxJLZEGoP$bZhlVw`;u|M|5WZbZZ?Nb@gw&G zk&WXzHTJ5I8KZerar(b#G=m<9RYu`~f+gKd2U|VIlvo!%_wNW(gt$3YyPA3#YomFD zg_g_1B-Oa?r2z^eFJuRdhj*j6$be+HLz8N!-LRI}_R*nXbX#|E5KxitC4v3XJ$ zrKp&?QttQ@u~jn0Lax0sFYE?! zkieLSfQ(C3SfLDFy#NU-7s6j2+1Uen(_`k5c2l1KKArDAXYX;zsPi+=%T%YPhRis3 zS#&OXyNwW3SPjr^7@_ujjx553W+Mk9YHju@hxko76S9}4XJr0Ru#%_JJ1;t@qiKmE z^=wM>n$1cLGdP1vg*{*Yi|}LkvTa!&n(`-4jxyU04j`7+T)3Mvuw{rXr#jIGmvP)7 zPS%yq&*2VH_7b?nY1UX3_xT|7XfdV56<(trry67iuo`DGpseB;?0&A5d1LBt+A)TN zb)dM~X3AaGy8~R`FC_a$PoiOGxK^~##qH78Ln)EA-#t0@x4n*cHHB~G^wiKgDb3Itkxn?lubf zu2cfs!&~gjwQuW5W3}b(!64CbLwJnx;z4jLmg4om%v(OU zvbR4aP-a_C*CpB$2Y5fgo-SRWO<5S9uI0f}X^ocXFM69|@p#S-9?Yq10=u$5d(){(T3=t1*MU_G1rzZj& zq7vR8$(Yb{c+^@Ypf+wb5|#aaf6%x#Jf_xP4cw9zd@O=eAY`$m#ceZ2DBtHVA?Jd^ zpMT(^txpB2tDN&`w0)%@dp+iF58Dx?g-|WEKkZG%}dlajvtgspA&W9`JGQlb6z~iXzn~S^^0q^u+}Kjup&ug)TP&3Zp?o7dw*~bEqhG|BdR@m zFU*HE5S##15aO4p*&eeXHUi$?Z;Y7@s#x%-lLXvm@$7o7!TxRHnrTXLnf)yAk@pwd zXEO-nA^oc|Ica5AFupAOe~Cw2eEwbi+d~P&y9D5a1Kiw&G<9Gw2u$eoX;ZYB(Ek9p C&BY1; From 6ea616382faf08c5e7bca9c15154c2aef979239a Mon Sep 17 00:00:00 2001 From: Guy Corbaz Date: Fri, 9 Oct 2026 11:36:24 +0200 Subject: [PATCH 11/12] =?UTF-8?q?docs(15-13b):=20cl=C3=B4ture=20=E2=80=94?= =?UTF-8?q?=20revue=20de=20code=20close=20(P2=20cibl=C3=A9e=20Haiku=200=20?= =?UTF-8?q?au-dessus=20de=20LOW,=20rapport=20perdu),=20rebas=C3=A9e=20sur?= =?UTF-8?q?=20la=2015-13a,=20gates=20rejou=C3=A9s=20=C3=A0=20froid,=20stat?= =?UTF-8?q?ut=20done=20(refs=20#552,=20refs=20#576)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- ...13b-sauvegarde-avant-import-persistante.md | 52 ++++++++++++++++++- .../epic-15-choix-autonomes.md | 7 +++ .../sprint-status.yaml | 3 +- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md b/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md index 3ffd1543..5bf8ced6 100644 --- a/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md +++ b/_bmad-output/implementation-artifacts/15-13b-sauvegarde-avant-import-persistante.md @@ -1,6 +1,6 @@ # Story 15.13b : la sauvegarde prise avant un import survit au redémarrage, et son échec est annoncé tel quel -Status: review +Status: done