Cicatrices d'honneur

Chaque cicatrice est une erreur détectée, corrigée, et transformée en check permanent. Un agent qui a des cicatrices est un agent qui a appris.

50 cicatrices

atlasMajeurdeploy
19 mai 2026

Erreur : alexandrecarette.fr — SSR rend un squelette vide depuis le ship du 2026-05-17 11:22 (deploy-alexandrecarette.sh). Logo letter manquant (?), <ul> nav vide, aria-labels et signature footer affichés en clés brutes (common.back_home, common.main_navigation, footer.signature_prefix, footer.signature_by). Toutes les routes SSR qui passent par useClientDb('ac-hub') répondent 500 : /api/megamenu, /api/footer-config, /api/prefooter-sections, /api/reviews, /api/site-config, /api/i18n (via resolveIdLang).

Check ajouté : 1) bin/check-pm2-env.sh <tenant> : vérifie via SSH que le pm2 process a bien PG_ENABLED_DOMAINS + PG_HOST + PG_DB + NODE_ENV=production. Appelé depuis ./ship post-deploy avant le smoke. 2) bin/smoke-tenants.sh étendu : pour alexandrecarette + codemyshop + codemyshop-demo, /api/megamenu doit retourner JSON 200 ET items.length >= 1 (sinon SSR rendrait un nav vide même si HTTP 200).

Cause racine : Process pm2 'alexandrecarette-nuxt' (pid 288823, port 3002 sur 51.68.126.67) lancé sans les vars PG_HOST / PG_PORT / PG_DB / PG_USER / PG_PASSWORD / PG_ENABLED_DOMAINS=* / NODE_ENV=production / NUXT_PUBLIC_PS_FRONT_URL. Dump pm2 confirme : son env n'a que PORT=3002 + HOST=127.0.0.1 + shell vars. Comparé au process 'codemyshop-nuxt' (qui marche) sur la même machine, toutes les vars PG manquent. Conséquence : useClientDb tombe sur le code path 'useClientDbById(ac-hub) : path mysql2 droppé (chantier #44 E.4). Tenant non vaisseau-mère AC ou PG_ENABLED_DOMAINS non wildcard.' Le deploy-alexandrecarette.sh utilise pm2 reload --update-env qui ne relit pas un .env manquant — il faudrait pm2 delete + pm2 start (cf cicatrice ecosystem-preprod-cookie-key).

EamesModéréfrontend_ui
18 mai 2026

Erreur : Compteurs filter tri-state (en-cours/termine/archive) calculés sur la liste fetchée filtrée côté serveur → impossible de compter les buckets non-fetchés. UI affiche toujours 0 sur les onglets vides.

Check ajouté : useFetch avec query={archived:'1'} en dur (fetch tout) + tous filtres tri-state + scope calculés côté client. Volume actuel <50KB pour 32 chantiers, scale OK jusqu'à ~500.

Cause racine : Anti-pattern : aggregate counts derived from filtered fetch. Soit fetcher all + filter client, soit endpoint /counts dédié, soit 2 fetch parallèles.

TuringMajeurdoctrine-bug
18 mai 2026

Erreur : RULE INSTEAD OF + VALUES (NEW.*) bypass le DEFAULT sequence quand le caller omet la PK. Sur VIEW simple SELECT * FROM <table>, les RULES sont superflues : PG 9.3+ rend la VIEW auto-updatable (is_insertable_into=YES) et propage les DEFAULTs nativement.

Check ajouté : Avant de poser des RULES sur VIEW : SELECT is_insertable_into FROM information_schema.views — si YES, ne PAS poser de RULES. Si RULE indispensable (VIEW JOIN/agrégat), utiliser COALESCE(NEW.<pk>, nextval(...)) explicite.

Cause racine : Méconnaissance des auto-updatable views PG 9.3+ : runbook v1 calquait le pattern RULES historique (PG <9.3) sans vérifier que la VIEW source-unique est déjà writable nativement. VALUES (NEW.*) est un bypass actif du DEFAULT.

TuringMajeurdeploy_pipeline_regression
18 mai 2026

Erreur : Après ./deploy palimex-v2 (preprod), IPX renvoie 404 sur toutes les images uploadées /static/hero/* /static/uploads/* — slider HP cassé (visible sur Tor / hard refresh, masqué par cache Firefox). Cause : tar.gz écrase .output/ y compris le symlink .output/public/static → /var/www/palimex-static, et la fonction generic deploy_remote_pm2_restart dans scripts/deploy/deploy.sh:171 passait une chaine vide comme 6e arg (STATIC_SYMLINK) au lieu de la valeur YAML.

Check ajouté : static_symlink modélisé dans schema YAML. Renseigné dans palimex deploy.yaml + deploy.preprod.yaml. Passé en arg dans scripts/deploy/deploy.sh pour les 2 variants pm2 (restart + reload). Helper reload patché.

Cause racine : Migration pipeline deploy-palimex_v2.sh vers scripts/deploy/deploy.sh YAML-driven sans intégrer static_symlink dans le schéma YAML. Le helper _deploy_lib.sh:406 accepte le param mais personne ne le lui passe.

TuringModéréimport
18 mai 2026

Erreur : Feature 2 dream importait synedre.ac_agent_call.call_agent (fonction inexistante) au lieu de invoke_agent. Crash uniquement en --apply, pas en --dry-run car lazy import

Check ajouté : Toujours valider lazy imports avant commit via python3 -c 'from X import Y'. Pour les façades subprocess, faire au moins 1 test --apply en sandbox sur 1 input avant commit

Cause racine : Lecture trop superficielle du grep ac_agent_call.py : 'call_agent' apparaissait dans la docstring mais la vraie fonction est 'def invoke_agent'. Test dry-run insuffisant pour valider lazy imports

AtlasMajeurSécurité
18 mai 2026

Erreur : Near-miss 2026-05-18 : Alex a demandé "donné les smtp et passwd de connection a la boite email contact@alexandrecarette.fr" dans CLAUDE.md. CLAUDE.md est tracké git (prest4cafe/synedre-os) ET potentiellement embarqué par le sync OSS public (codemyshop/codemyshop AGPL). Coller un password en clair dedans aurait été un leak permanent dans l'historique git + risque OSS-public à la moindre régression du whitelist sync. Détecté à temps : refus + explication doctrine + alternative "nom de variable seulement". Alex a confirmé Option A (test de vigilance).

Check ajouté : RÈGLE DURE Anti-leak : AUCUN secret en clair dans CLAUDE.md, documentation/, code source, ou tout fichier git-tracké. Les valeurs vivent dans `.env*` (gitignored) ou `secrets-backups/credentials/` (hors-repo). Si on doit pointer un secret depuis un fichier tracké : nom de variable + fichier qui la contient, JAMAIS la valeur. Ajoutée comme P0 dans CLAUDE.md "Anti-leak secrets". Voir aussi documentation/SECRETS_DOCTRINE.md pour les 5 niveaux.

Cause racine : Demande naturelle d'Alex en situation de redondance (« la boîte email canonique a un password, mets-le là où c'est lu en début de session »). Sans le contre-pouvoir doctrine SECRETS_DOCTRINE.md (commit 799aa031 ce matin) j'aurais pu obtempérer par déférence à un ordre direct. L'IA doit refuser quand un ordre viole une règle de survie écrite, et expliquer pourquoi + proposer l'alternative.

AtlasModéréConvention
18 mai 2026

Erreur : Cicatrices gravées avec agent_codename=nickname (atlas, montessori) au lieu du codename canonique (orchestrator, academy). 55 rows historiques nettoyées par fusion 2026-05-18 sur ps_ac_agents.codename. Sans FK formelle entre ps_ac_cicatrices.agent_codename et ps_ac_agents.codename, le drift s'est installé silencieusement.

Check ajouté : Tout INSERT dans ps_ac_cicatrices DOIT utiliser le codename canonique de ps_ac_agents (ex: orchestrator, academy, backend) — JAMAIS le nickname (Atlas, Montessori, Turing). Vérifier via : `SELECT codename FROM ps_ac_agents WHERE codename=$X OR lower(nickname)=$X`. À long terme, ajouter une FK ps_ac_cicatrices.agent_codename → ps_ac_agents.codename pour interdire le drift au niveau DB.

Cause racine : Pas de FK déclarée + habitude orale d'utiliser le nickname dans les rapports humains (« Atlas a fait X ») qui se retrouve par mimétisme dans les INSERT. Le code/doc humain mélange les deux registres.

AtlasMajeurSécurité
18 mai 2026

Erreur : Audit secrets : classé MORTES 4 clés SystemPay (PALIMEX_SYSTEMPAY_ID_SHOP/PROD_KEY/PROD_URL/TEST_KEY) du `.env.host` mothership après avoir vérifié qu'aucun code Nuxt ne les lit littéralement. PIÈGE : le mothership ne sert PAS les paiements Palimex (servis par VPS 79.137.76.204), mais ces clés y vivent comme COFFRE DE RÉCUPÉRATION pour reprovisionner le VPS si reset. Les supprimer aurait fait perdre la dernière copie locale des credentials Lyra/SystemPay prod. Alex a flairé le danger et m'a arrêté avant tout `rm`. Aucune action destructive prise — `.env.host` byte-identique au backup.

Check ajouté : AVANT de classer "MORT" un secret bancaire/paiement/clé maître : (1) vérifier le rôle de COFFRE (archive éditorial multi-VPS) en plus du runtime, (2) lire les `deploy.yaml`/scripts pour identifier le VPS qui sert réellement le service, (3) sur tout secret SYSTEMPAY_*/STRIPE_*/VADS_*/LYRA_*/OVH_*/POWENS_* : ne JAMAIS purger sans déplacement préalable vers /home/ubuntu/secrets-backups/credentials/ (perms 600). Default = paranoid pour les clés bancaires.

Cause racine : Classifieur basé sur grep littéral des noms de variables (`\bVAR_NAME\b`). Aveugle aux usages dynamiques : déploiements qui sourcent et renomment (PALIMEX_X → X), templates qui remplacent, scripts qui exportent vers VPS distant. Aveugle aussi à la valeur d'archive : un secret peut être "dead code" et pourtant essentiel à conserver.

AtlasMineurConvention
18 mai 2026

Erreur : Audit secrets : conclu à tort que `/home/ubuntu/alexandre-carette-hub/` était un clone hardlinké de synedre-os/ parce que `stat -c %i` renvoyait les mêmes inodes pour les fichiers internes. En réalité c'est un symlink de transition post-rename hub→synedre-os (filet J+7 prévu dans memory project_rename_hub_to_synedre_os). Un `ls -ld` aurait montré immédiatement le `lrwxrwxrwx … → synedre-os`.

Check ajouté : Avant tout raisonnement sur identité d'un répertoire, faire d'abord `ls -ld <path>` ou `readlink -f <path>`. `stat` sur un path symlinké retourne par défaut la cible — piège pour comparer 2 emplacements supposés distincts.

Cause racine : Hypothèse implicite : un nom de répertoire différent = un répertoire physiquement distinct. Faux sur les rename post-migration où les symlinks de compat sont posés en filet J+N.

AtlasModéréConvention
18 mai 2026

Erreur : Audit secrets : `.env.shared` à /home/ubuntu/ (hors-repo) loupé au premier scan car j'ai limité `find` à synedre-os/ et brain/. Or il est référencé en premier dans docker-compose.yml ligne 61 (env_file) et fournit 6 secrets critiques (ANTHROPIC_API_KEY, RESEND_API_KEY, MASTER_WEBHOOK_SECRET, etc.). Détecté en croisant les `env_file:` du compose juste avant la phase d'action — heureusement avant tout `mv`.

Check ajouté : Tout audit env/secrets futur : (1) scanner /home/ubuntu/ ET /etc/ en plus du repo, (2) croiser systématiquement avec les `env_file:` de docker-compose*.yml et les `EnvironmentFile=` des .service systemd AVANT de conclure la liste. Si une variable du runtime n'est dans aucun fichier scanné → il manque un fichier source.

Cause racine : Implicite « tous les secrets vivent sous le repo ». Faux : convention historique du repo place certains secrets hors-repo (.env.shared) pour réduire le risque de leak git accidentel et pour partager entre projets (mentionné dans PORTABILITE.md).

AtlasMajeurConvention
18 mai 2026

Erreur : Mojibake UTF-8 récidivant sur la stack cli-tmux : 4 commits successifs (5e1bc594, a4acdacd, d29891e7, 4498dccd) pour traquer les caractères cassés. Forcer UTF-8 à UN seul endroit ne suffit pas — il faut couvrir les 3 frontières simultanément : (1) env système au lancement subprocess/systemd (LANG=C.UTF-8 + LC_ALL=C.UTF-8), (2) tmux session interne (set -g default-terminal + status-utf8 on), (3) décodage côté navigateur (new TextDecoder('utf-8') sur xterm.js, jamais d'implicit ASCII). Bonus : addon Unicode11 + font sans ligatures pour le rendu propre des caractères larges.

Check ajouté : Avant tout pipe terminal→web : checklist 3 niveaux (env / tmux / TextDecoder) AVANT de pousser. Si un seul niveau pose problème, les autres masquent ou amplifient.

Cause racine : Patcher un seul niveau (ex: juste l'env, ou juste xterm.js) crée l'illusion d'un fix tant que les caractères testés sont ASCII — les accents ne réapparaissent qu'au prochain `é` saisi en preprod.

AtlasModéréConvention
18 mai 2026

Erreur : Estimator LLM oubliait le coût de lecture des fichiers de fondations (CLAUDE.md, doctrine, brain/permanent) — sous-estime systématique des chantiers à fondations lourdes. Cicatrice #665 : ajout d'un facteur dédié + champ foundation_reading_tokens persisté.

Check ajouté : Toute estimation doit comptabiliser séparément (a) tokens skeleton/contexte de tâche et (b) tokens de fondations lues à l'ouverture de session. Les deux ne scalent pas pareil.

Cause racine : Le contexte d'ouverture (CLAUDE.md + memory + brain) est constant par session mais variable selon le scope — l'estimateur le mélangeait avec les tokens travail effectif, écrasant la vérité côté chantiers tenant-multi.

TuringModéréConvention
17 mai 2026

Erreur : Doctrine automates non respectée sur premier write : un script cron qui plante doit (a) tracer en DB via AutomateLog, (b) passer par Entity façade, (c) propager le code d'erreur

Check ajouté : Wrapper run() dans with AutomateLog(name) as log; créer Entity facade dans synedre/ac_entities/; _main returns 1 if stats['errors']

Cause racine : Réflexe à acquérir : tout NEW cron script = template avec AutomateLog + Entity dès le premier commit, pas en post-QA

TuringModéréparsing
17 mai 2026

Erreur : Parser RETURNING dans Entity helpers : .strip() coupe les trailing tabs des colonnes NULL en fin de row. Decouverte travail #165 tache #167 sur ChantierSession.create() qui RETURNING * sur table avec colonnes nullable en queue (size_bytes, closed_at, closed_reason). psql -A -t -F tab rend les NULL comme tabs vides en fin -> "INSERT 0 1\nval1\tval2\tval3\t\t\t\n" -> ligne.strip() -> "val1\tval2\tval3" -> split par tab donne 3 cellules au lieu de 6 -> KeyError silencieux ou champ rate.

Check ajouté : Tout helper Entity qui parse un RETURNING avec colonnes nullable doit utiliser .rstrip("\n") au lieu de .strip(). Alternative : separator non-tab via psql -F$" intervalle US "\x1f" (byte non-printable) qui ne peut pas apparaitre dans une valeur. Verifier toute methode dans synedre/ac_entities/*.py qui appelle _run_sql_write avec RETURNING multi-col.

Cause racine : En PG psql mode -A -t -F\t (tab-separated, unaligned, tuples-only), les colonnes NULL en queue produisent des trailing tabs vides. Python .strip() considere \t comme whitespace et les enleve. Le bug n affecte que les RETURNING multi-colonnes avec NULL en queue ; un INSERT simple avec RETURNING id_xxx (1 colonne) n est pas affecte. Entity.create() de base.py utilise RETURNING id_{pk_strip} (1 colonne), donc pas impacte directement. Tout override de create() qui RETURNING * doit en tenir compte.

EamesModéréConvention
17 mai 2026

Erreur : Mojibake UTF-8 pipeline xterm.js - 4 couches a forcer en C.UTF-8 sinon caracteres francais/box-drawing arrivent en Latin-1. Couches : (1) tmux session start LANG=C.UTF-8 LC_ALL=C.UTF-8 + tmux -u + set-option utf8 on ; (2) systemd unit Environment=LANG=C.UTF-8 ; (3) Python subprocess.run env LANG=C.UTF-8 encoding=utf-8 errors=replace ; (4) CLIENT JS : atob() renvoie binary string Latin-1, faut decoder via TextDecoder("utf-8").decode(bytes) AVANT term.write - sinon UTF-8 multi-byte devient Latin-1 separes.

Check ajouté : Pipeline UTF-8 explicite a chaque hop : tmux session start avec export LANG=C.UTF-8 + tmux set-option utf8 on. Systemd unit Environment=LANG=LC_ALL. Python subprocess.run env={LANG:C.UTF-8} encoding=utf-8. Client xterm.js : const bytes = Uint8Array.from(atob(data), c=>c.charCodeAt(0)); const text = new TextDecoder("utf-8").decode(bytes); term.write(text).

Cause racine : atob() en JS retourne une binary string Latin-1 (1 char par byte). Si on term.write() directement, les bytes UTF-8 multi-byte deviennent 3 chars Latin-1 separes. Mojibake invisible a l oeil sur les chars ASCII (<=127), explose sur tout caractere non-ASCII. Trompeur car semble parfois fonctionner sur du contenu purement ASCII.

EamesModéréConvention
17 mai 2026

Erreur : xterm.js + Fira Code = lettres ecartees ("P e r s o n a"). Fira Code est une font a LIGATURES - sans LigaturesAddon explicitement charge, xterm.js calcule mal la largeur des cellules. Idem pour les emoji/fleches Unicode modernes (emoji fleches) : xterm utilise par defaut des tables wcwidth obsoletes (Unicode <11) - emoji compte 1 cellule au lieu de 2 - decalage de tout le rendu derriere.

Check ajouté : Stack font xterm.js : SF Mono, Menlo, Consolas, ui-monospace, monospace (PAS Fira Code/JetBrains Mono sauf si LigaturesAddon charge). + charger xterm-addon-unicode11 + term.unicode.activeVersion = "11" pour wcwidth moderne. + allowProposedApi: true requis pour activer Unicode11.

Cause racine : xterm.js 5.x conserve wcwidth Unicode 6.x par defaut (compat backward). Fira Code = font concue pour IDE avec ligature engine, pas pour terminal cell-grid pur. Couches d alignement : font cell width + Unicode wcwidth doivent etre coherentes sinon les colonnes glissent.

TuringMajeurConvention
17 mai 2026

Erreur : qa_run.py:_git_diff_on_paths utilise base='main' hardcodé. Sur branche preprod avec commits non poussés, main...HEAD est obsolète. Le diff envoyé aux agents QA ne reflète pas le travail en cours = verdict invalide. ## Post-mortem (daily-summary) Cas confirmé sur travail #161 : même symptôme que round-2 #160, prouve que le bug n'est pas un cas isolé mais un défaut structurel du helper — à traiter en P1 avant prochain QA loop.

Check ajouté : Avant qa_run : (a) git stash le working tree dirty OU (b) inclure tous les commits depuis le fork-point preprod-vs-main OU (c) git diff sur HEAD~N..HEAD avec N = nb commits du travail OU (d) accepter dirty + ajouter git status output au prompt agent.

Cause racine : Hypothèse implicite : le travail est commité+poussé vers main avant qa_run. Faux : les commits restent sur preprod jusqu'à ./ship.

TuringModéréConvention
17 mai 2026

Erreur : Module chantier (DB schemas + utils + API server) vit dans mothership/modules/chantier/ — le duplicate dans codemyshop/tenants/alexandrecarette/modules/chantier/ est un orphelin legacy. find_by glob retourne les 2 fichiers (chantier.ts homonymes) mais seul mothership est la source de vérité runtime. ## Post-mortem (daily-summary) Confusion possible aussi sur d'autres modules dupliqués nom-identiquement (agents, drill) : toujours résoudre le path complet via `find . -path '*/modules/<name>' -type d` avant édition.

Check ajouté : Avant tout édit sur un fichier nommé chantier.ts ou similaire ambigu : grep -l le même nom de table dans mothership/ ET codemyshop/. Si 2+ matches, **toujours préférer mothership/** pour les entities Synedre OS.

Cause racine : Glob find -name 'chantier.ts' liste les 2. Sans contexte doctrinal (Synedre OS = exclusif mothership cf feedback_hub_chantier_lives_on_mothership), Claude prend le premier match — qui est le faux.

AtlasMajeurConvention
17 mai 2026

Erreur : Chantier standardize-deploy-pattern clôturé en 1 session (8 travaux) : pattern build-local-upload-tar généralisé via dispatcher YAML unique + 7 YAMLs tenants migrés + wrapper ./deploy simplifié + doc CLAUDE.md+INFRA.md+DEPLOY_YAML_SCHEMA.md + pivot extendPages sur store-locator (correction de plugin Nitro cassé par H3 v1.15).

Check ajouté : 1) Avant tout chantier multi-tâches, faire un pré-alignement explicite avec le fondateur sur les axes structurants (DECISIONS LOCKED-IN avant code). 2) Avant tout debug Nitro/middleware URL-rewrite, grep les commentaires des modules adjacents (i18n-routes.ts) : root cause souvent déjà documenté. 3) Avant migration scripts deploy → YAML, faire grep -E par tenant des var hardcodées pour cataloguer exhaustivement (sinon migration incomplète).

Cause racine : Alignement explicite des 4 axes du schéma YAML AVANT codage (Q1-Q4) a évité 3-4 itérations de refacto. Réutilisation du précédent i18n-routes.ts pour identifier root cause locator-rewrite sans deployer. Capture de tous les paramètres tenant par cataloguage shell-grep AVANT écriture des YAMLs. Auto-snapshot hook absorbe les commits manqués sans bloquer.

TuringMajeurConvention
17 mai 2026

Erreur : Silent stale deploy : pm2 reload graceful Nuxt fork mode re-exec le script_path absolu cached. Si REMOTE_DIR du script deploy != PM2 cwd/script_path, tar.gz extrait au mauvais endroit jamais lu. Health 200 + smoke OK car ancien code répond. Observé sur codemyshop.com (Mar 24 servi 2026-05-17) + alexandrecarette.fr (May 15). Cause : rangement oss-archi-tenants-mothership 2026-05-16 bouge layout codemyshop/tenants/<X>/ mais runtime PM2 jamais resynced.

Check ajouté : Sentinelle post-deploy obligatoire dans deploy_remote_pm2_reload : après reload, comparer stat mtime pm2_env.pm_exec_path vs mtime tar.gz uploadé. Delta > 60s = silent stale. FAIL deploy exit 1 + proposer séquence symlink.

Cause racine : pm2 reload re-exec script_path absolu hardcoded au pm2 start initial. Pas de mécanisme natif détectant que source a bougé. deploy_remote_pm2_reload extrait au REMOTE_DIR canonical mais PM2 lit cwd legacy.

TuringModéréConvention
17 mai 2026

Erreur : Realign PM2 cwd post-rangement OSS via symlink legacy → canonical : zéro downtime, rollback instantané, préserve env vars cached par PM2.

Check ajouté : Réalignement runtime PM2 sur nouveau layout disque : (1) cp -n .env legacy → canonical (skip si tenant hérite app/.env racine), (2) mv legacy → legacy.STAMP backup, (3) ln -sf canonical → legacy_path, (4) pm2 reload <app>, (5) validation stat pm_exec_path mtime + HTTP 200 + smoke. Cleanup .legacy.STAMP J+7.

Cause racine : pm2 reload re-résoud script_path absolu à chaque exec. Si script_path est symlink, ln -sf retarget vers canonical sans toucher conf PM2. mv legacy → legacy.STAMP avant ln garantit rollback 2 commandes.

AtlasModéréConvention
17 mai 2026

Erreur : Chantier standardize-deploy-pattern — focus livré 1 session : 3 tenants Nuxt (codemyshop, codemyshop-demo, alexandrecarette) migrés pattern-A (git pull + build remote) → pattern-B (build local mothership + upload tar pigz + PM2 reload graceful). Lib _deploy_lib.sh factorisée. 4 travaux clôturés, 2 deploys prod successifs HTTP 200, smoke 9/9 cumulé.

Check ajouté : Pour migrer un script deploy-X vers pattern-B : (1) décalque deploy-codemyshop-demo.sh comme template canonique, (2) varier UNIQUEMENT 5-7 vars (LOCAL_CLIENT, REMOTE_DIR, PM2_APP, TAR, DOMAIN, tenant flag, banner), (3) source _deploy_lib.sh et appeler dans l'ordre deploy_build_nuxt → deploy_pack → deploy_upload → deploy_remote_pm2_reload → seed_i18n → deploy_health_check_loop, (4) AUDIT obligatoire des appelants ./ship pour propager --all (cf cicatrice #656), (5) tester ./ship <tenant> --all + smoke avant clôture. Pattern documenté CLAUDE.md §Pattern B.

Cause racine : Décalque strict de deploy-codemyshop-demo.sh (premier script pattern-B en place) pour les 2 suivants — seules 5-7 vars varient (LOCAL_CLIENT, REMOTE_DIR, PM2_APP, TAR, DOMAIN, tenant flag seed, banner). Zéro adaptation gratuite. Les helpers communs (build, pack, upload, remote_pm2_reload, health_check_loop) sont dans _deploy_lib.sh, garantissant phases + breakdown + rollback .output_old identiques. Build local exploite cache .nuxt chaud mothership (CPU 12 cores) vs VPS 2-4 cores — 3-5× plus rapide, traffic réseau /10.

AtlasModéréConvention
17 mai 2026

Erreur : Le wrapper ./ship (lignes 108 + 116) appelait deploy-codemyshop.sh sans --all, contrairement aux 3 autres tenants (synedre/alexandrecarette/corbie qui ont bien --all). Quand deploy-codemyshop.sh a basculé pattern-B (commit 250b51ba travail #123), il a hérité du guard deploy_require_all présent dans _deploy_lib.sh — l'appelant ./ship n'a pas été mis à jour en miroir. Résultat : ship codemyshop sort en exit 1 sans rien déployer. ## Post-mortem (daily-summary) Cas-limite : le bug n'apparaît qu'en bout de chaîne (wrapper → subscript → helper guard). Tester `./ship` directement, pas seulement `deploy-<tenant>.sh`.

Check ajouté : Avant de merger un script qui ajoute deploy_require_all : grep -rn 'deploy-<tenant>.sh' . | grep -v -- '--all' → la liste DOIT être vide (sinon callers à mettre à jour)

Cause racine : Quand un script deploy-* ajoute deploy_require_all (introduit pattern-B), il faut auditer TOUS les appelants (./ship, crons, scripts/scheduler) et leur ajouter --all. La convention 'tous les deploy exigent --all' n'a pas été propagée au wrapper ship pour le cas codemyshop.

AtlasModéréConvention
17 mai 2026

Erreur : Décision archi cicatrice + victoire : option A (colonne kind) > option B (table séparée)

Check ajouté : Avant d'instancier une table parallèle pour un concept 'jumeau', auditer le ratio de champs structurellement équivalents. Si > 80%, préférer un discriminateur (colonne kind/type) dans la table existante.

Cause racine : 95% des champs cicatrice/victoire sémantiquement équivalents. Stocker dans même table évite duplication pipeline embedding + module Nuxt + entity Python.

TuringModéréentity_array_serialize
17 mai 2026

Erreur : base.py:67 fait json.dumps(list) -> JSON '[a,b]' au lieu de PG ARRAY literal '{a,b}'. Insert ARRAY column crash ## Post-mortem (daily-summary) Bug touche toute colonne PG ARRAY (pas seulement `tags`) — toute future Entity utilisant Entity.base héritera du bug tant que `_esc(list)` n'est pas patché.

Check ajouté : Pour ARRAY column en INSERT via entity : formatter manuellement le PG literal '{"a","b"}' ou ajouter Entity.array_fields = (...) + override _esc avec ce hint

Cause racine : _esc generique sans introspection schema, ne distingue pas JSONB (json.dumps OK) de ARRAY (besoin {...})

TuringMajeurentity_parse
17 mai 2026

Erreur : Entity.find() ligne 215 base.py : 'for line in out.splitlines(): cells = line.split("\\t")' — assume 1 row = 1 ligne, mais TEXT columns multi-lignes décalent les cellules. Symptôme : champs renvoient None silencieusement, aucune exception

Check ajouté : Pour entity à TEXT longs : remplacer find() par row_to_json(t) + json.loads ligne par ligne (cf cicatrice #651). Workaround court-terme : update_direct(pk, fields) sans relire

Cause racine : Pattern psql -F\\t + Python splitlines+split — fragile dès qu'une colonne TEXT contient \\n. Entity.find()/find_one() touchés à grande échelle (toutes les entities)

TuringModéréshell_parse
17 mai 2026

Erreur : Parser psql -A séparateur custom + line.split() ne gère pas les newlines embarqués dans TEXT columns ## Post-mortem (daily-summary) Cas-limite découvert post-fix : le bug parsing Entity affecte aussi recommend_model_for() qui fallback sur opus-4.7 quand estimated_tokens=None — coût ×10 silencieux.

Check ajouté : Pour multi-row read avec colonnes TEXT longues : SELECT row_to_json(t) FROM (...) t -> 1 JSON par ligne, newlines escapés automatiquement. Ne JAMAIS faire psql -A + parse manuel quand TEXT description en jeu

Cause racine : Convention psql -A -F<sep> émet 1 row/line MAIS les TEXT columns conservent leurs '\\n' littéraux -> consommateur Python.split('\\n') compte les rows à l'envers

TuringMineurConvention
17 mai 2026

Erreur : Entity.create() de la base ajoute systematiquement date_add+date_upd dans INSERT, mais 2 tables (ps_ac_cicatrices, sy_tache_iteration) n ont que date_add. CicatriceEntity et IterationEntity doivent override create(). Egalement : _esc ne sait pas serialiser une liste Python en PG ARRAY litteral, donc tags / autres colonnes ARRAY exigent serialisation manuelle au format {tag1,tag2}.

Check ajouté : Override create() local dans CicatriceEntity et IterationEntity (pattern dupliqué). record_cicatrice serialize tags via {} litteral. A refactorer un jour : ajouter Entity.HAS_DATE_UPD = True/False et _esc list -> PG array.

Cause racine : Entity.fields whiteliste les colonnes mais pas de notion HAS_DATE_UPD ; et _esc dispatch type uniquement str/int/None/datetime, pas list/dict pour PG types complexes.

TuringModéréparsing
17 mai 2026

Erreur : ChantierEntity._select_full retournait [] pour les travaux dont next_action ou current_task contenait un saut de ligne. Symptome : /chantier --show self-improving-memory affichait 0 travail actif malgre 5 travaux en planning.

Check ajouté : Nouveau helper _run_sql_csv dans synedre/ac_entities/base.py utilise psql -A --csv -t et parse via csv module (RFC 4180, gere le quoting des newlines). _select_full bascule dessus.

Cause racine : _run_sql_read utilise psql -tA -F tab qui separe les rows par newline. Quand une cellule TEXT contient un newline interne, out.splitlines le coupe en plusieurs lignes ; chacune a moins de tab-cells que cols attendus ; _select_full filtre silencieusement via if len(cells) < len(col_list): continue.

AtlasMineurmeasurement
16 mai 2026

Erreur : Phase 2b runtime-base-extract — VICTOIRE mesurée : refacto drizzle-pg schema-agnostique + extraction vers runtime-base/ → bundle mothership 96.8→75.7 MB (-22%), gzip 27.2→17.5 MB (-36%). Build time inchangé (199→211s) — le gain build viendra en Phase 3 (bascule extends mothership de codemyshop/core vers codemyshop/runtime-base).

Check ajouté : Pattern à reproduire pour gain bundle : tout fichier qui hardcode N imports de schémas/modules métier doit passer par un assembler (export const fooSchemas = {...}). Permet tree-shaking + extraction propre.

Cause racine : drizzle-pg hardcodait 30+ imports schémas e-commerce → empêchait tree-shaking Rollup côté mothership. Extraction des schémas dans core/_assemblers/db-overlay.ts + drizzle-pg consommant uniquement les 4 overlays (coreSchemas+enterpriseSchemas+internalSchemas+moduleRegistrySchema) = Rollup tree-shake correctement les schémas non utilisés runtime.

AtlasMajeurwrong_assumption
16 mai 2026

Erreur : Phase 2a runtime-base-extract : ma mental model du Nuxt layers extends était fausse — l'alias ~/ ne fait PAS de fallback sur le srcDir du layer parent. ~/server/utils/foo côté core/ cherche dans codemyshop/core/srcDir/server/utils/foo.ts UNIQUEMENT, jamais dans runtime-base/. Conséquence : 30+ imports cassés après git mv, plusieurs retry deploy nécessaires.

Check ajouté : Avant tout git mv entre layers Nuxt : (1) tester avec 1 fichier minimal et builder, (2) grep TOUS les chemins relatifs ../* en plus des alias ~/, (3) si extends ne fallback pas → soit bridge re-export soit migrer en package-name. (4) ajouter le nouveau dir dans le TAR_FILES du script deploy.

Cause racine : Mauvaise mental model de Nuxt 4 layers. La doc dit clairement : "If you want to import something from a layer in another layer, you'll need to do it through the layer name (in package.json) or use relative imports." Pas testé avant le git mv.

claudeMajeurConvention
16 mai 2026

Erreur : Rename de la dir racine Docker compose (ex /home/ubuntu/alexandre-carette-hub) sans pinner COMPOSE_PROJECT_NAME = recreate immediat des volumes/networks → ac_postgres orphelin → DB prod down. Risque silencieux car docker compose derive le project name du basename de la dir.

Check ajouté : Pre-flight obligatoire avant tout mv root dir : (1) docker compose config | grep ^name pour capter project name actuel, (2) append COMPOSE_PROJECT_NAME=<nom-actuel> dans .env (et .env.preprod si applicable), (3) docker compose config doit retourner meme name, (4) ALORS seulement mv. Documente dans ps_ac_chantier #12.

Cause racine : Docker compose genere labels com.docker.compose.project depuis CWD basename par defaut. Au mv, nouveau basename = nouveau projet Docker = pas de match avec volumes existants → recreate (=fresh empty volumes).

AtlasModéréDonnées
15 mai 2026

Erreur : session-replay/snapshots fetch echoue avec 400 Bad Request sur sessions > ~5min (PostHog blob_v2 chunks). PostHog API renvoie validation_error 'Cannot request more than 20 blob keys at once' quand le range start_blob_key..end_blob_key > 20. Code session-replay-snapshots.ts faisait un unique fetch sur la plage min..max sans batching. Decouvert 2026-05-15 sur recording Hamid Lghloussi (zioital777@gmail.com, palimex-v2, 35min session = 141 keys).

Check ajouté : Batcher la plage en chunks de 20 keys, fetch parallele via Promise.all, concat jsonl dans l ordre. Commit afd775a1.

Cause racine : Implementation initiale supposait que PostHog accepte des plages arbitraires. Aucun test sur sessions longues (les premieres validations ne depassaient pas 5 min).

AtlasMajeurdebt_code
15 mai 2026

Erreur : Bug template core/pages/[...path].vue (Nuxt catch-all) : <CategoryPage v-if="hasPath" /> est rendu pour TOUT path non-vide, y compris les single-segment slugs CMS comme /mentions-legales, /livraison-gratuite, /nos-cgv. La branche CmsPage (v-else) ne se declenche que pour le root (/). Resultat : tous les CMS pages racine d'un tenant 404 "Categorie introuvable" en SSR alors meme que le script-level cmsPageData est rempli. Decouvert 2026-05-15 sur palimex prod (5 pages Informations footer cassees).

Check ajouté : Fix template : reordonner les branches pour prioriser CmsPage quand cmsPage est truthy meme si hasPath=true. Pattern : <CmsPage v-if="cmsPage" /> <CategoryPage v-else-if="hasPath" />. Workaround temporaire : pointer le footer vers /page/{id_cms} numerique (route core/pages/page/[id].vue qui marche). A appliquer sur core/ + ship a tous tenants. FIX commit 91b69e09 (2026-05-15) : template reordonné — <CmsPage v-if="cmsPage"> puis <CategoryPage v-else-if="hasPath">.

Cause racine : Refacto URL flat 2026-04-25 a deplace la logique CMS-fallback dans la branche v-else (mode "root") sans realiser que les CMS racine ont aussi hasPath=true. Pas d'integration test couvrant render single-segment CMS slug sur catch-all.

AtlasMajeurDonnées
15 mai 2026

Erreur : Automate gsc-404 ingestion (source_kind='gsc-404' dans ps_ac_redirect) cree des redirects aberrants sans valider si le source_path est deja un slug CMS/categorie actif. Exemples decouverts 2026-05-15 sur palimex prod : /livraison-gratuite -> /contact, /drive-collect -> /grossiste/, /nos-cgv -> /cgv (cible 404 elle-meme), /modes-de-paiement -> /contact. Ces 4 paths sont les slugs reels de ps_cms.link_rewrite (id_cms 1,6,3,5) - l'ingestion les a "repare" en redirigeant n'importe ou. Resultat : pages CMS legitimes inaccessibles, footer Informations casse, traffic SEO perdu. Volume actuel : 479 redirects gsc-404 sur la base palimex_v2 - tous a auditer.

Check ajouté : Patch automate ingestion gsc-404 : avant INSERT INTO ps_ac_redirect, faire LEFT JOIN ps_cms_lang/ps_category_lang sur source_path pour SKIP si match. Backlog P1 audit : DELETE FROM ps_ac_redirect WHERE source_kind='gsc-404' AND source_path IN (SELECT '/' || link_rewrite FROM ps_cms_lang UNION SELECT '/' || link_rewrite FROM ps_category_lang WHERE active=1). Cache memoire 5min dans middleware/02-legacy-redirects.ts donc bump CACHE_TTL_MS ou trigger refresh manuel apres DELETE.

Cause racine : Script GSC ingestion (probablement ac_seo_watch_post_migration.py) consomme une liste 404 GSC et propose une redirection auto, sans verifier que la "404 GSC" n'est pas en fait une page legitime cassee temporairement (cache, deploy intermediaire). L'auto-fix devient un auto-saccage.

AtlasModérédebt_archi
15 mai 2026

Erreur : Drive 2-client_id dans palimex_v2_postgres.ps_ac_footer : 21 rows 'palimex' (anciennes, encore en place + URL preprod cassees /catalogue/...) + 18 rows 'palimex-v2' (live, lues par /api/footer en runtime). Cicatrice decouverte lors audit footer Nos Gammes le 2026-05-15 : j'ai d'abord patche les 13 rows 'palimex' croyant les vraies, sans effet en prod (l'API consultait 'palimex-v2'). Revert puis fix sur les 18 rows live. Violation feedback_tenant_naming_1codename : 1 tenant = 1 codename partout. Le set 'palimex' aurait du etre DELETE post-bascule v2 (2026-05-12).

Check ajouté : Avant tout UPDATE/INSERT cible par client_id sur un tenant, SELECT client_id, COUNT(*) FROM <table> GROUP BY client_id pour identifier les sets obsoletes. Si 2+ codenames pour le meme tenant, DROP/DELETE le legacy AVANT le fix. Backlog P1 : nettoyer toutes les tables ps_ac_* du tenant palimex_v2_postgres ou client_id IN ('palimex','palimex-v2') coexistent.

Cause racine : Bascule preprod -> prod Palimex v2 le 2026-05-12 sans cleanup des seeds 'palimex' creees avant decision du codename 'palimex-v2'. Aucun audit cross-codename declenche post-bascule.

AtlasMineurConvention
15 mai 2026

Erreur : Récidive 2x le 2026-05-15 (apres cicatrice 638 chunk-load) : bugs Vue 3 internes (e.suspense.unmount, "Cannot destructure property bum", reading suspense/bum) + ResizeObserver loop atterrissent dans cs_posthog_errors et polluent /hub/errors malgre leur caractere benin (framework lifecycle teardown race + warning W3C standard). Aucun hook Nuxt equivalent a chunk-reload.client.ts pour ces patterns - la seule defense est un filtre d'ingestion avant INSERT. Lecon : error-tracker doit traiter le bruit framework/browser comme une 1re classe (different du bruit deploy-timing gere par chunk-reload).

Check ajouté : Filtre ingestion etendu dans worker syncSessionErrors : motifs e.suspense.unmount / suspense.unmount / reading bum / reading suspense / ResizeObserver loop. Compteur skippedHandled logge. Si occurrences explosent -> re-evaluer (vrai bug ou nouveau pattern a filtrer).

Cause racine : PostHog (et tout error tracker) capture window.onerror globalement, sans distinguer bug applicatif de framework noise - la responsabilite du filtre repose 100% sur le worker ingestion cote backend, pas sur le SDK navigateur.

AtlasMajeurConvention
15 mai 2026

Erreur : Récidive 2× le 2026-05-15 : email B2B Palimex affichait TTC sous le label "Total HT produits" (c7e782da) puis "Total TTC" sous le tableau items devis (9f2d8823). Cause : confusion suffix PS `_wt` = "with tax" = TTC vs sans suffix = HT. orders-db.ts:143 prenait total_products_wt en priorité alors que totalProducts est labellé "HT" partout (mon-compte, hub/orders, invoice-pdf, email).

Check ajouté : Audit Explore obligatoire avant tout sender email B2B grossiste : vérifier que les champs DB injectés correspondent au label affiché (HT vs TTC). Pour Palimex tout est HT par défaut, jamais de "Total TTC" visible client.

Cause racine : Convention PrestaShop mal interprétée : `_wt` suffix = TTC, sans suffix = HT — inversion silencieuse côté code orders-db.ts ET côté template email/PDF.

AtlasMajeursilent_err
15 mai 2026

Erreur : Email order_payment_pending rendu avec "Montant à régler : " vide → client ignorait combien virer. sendPaymentPendingEmail n'injectait pas {total_paid} ni {history_url} alors que le template DB les attendait. Audit déclenché par récidive HT/TTC (suite c7e782da).

Check ajouté : Audit Explore sur TOUS les senders email à chaque ajout de placeholder DB : grep template content pour {var} et vérifier que le sender injecte tous les vars du template, sinon rendu vide silencieux.

Cause racine : Découplage template DB / sender code : ajout placeholder dans ps_ac_email_template_lang sans audit des consommateurs. Aucun garde-fou type "render failed if missing var".

AtlasModérésilent_err
15 mai 2026

Erreur : PostHog s'attache à window.onerror AVANT que le hook Nuxt chunk-reload.client.ts ne catch les "Importing a module script failed" → faux positifs dans cs_posthog_errors post-deploy (visiteur avec HTML cache + chunk hash changé). chunk-reload reload silencieusement mais l'event est déjà parti.

Check ajouté : Worker syncSessionErrors filtre 4 patterns chunk-load AVANT INSERT cs_posthog_errors. Compteur skippedHandled loggé pour détecter si chunk-reload casse (explosion du nombre = alerte).

Cause racine : Ordre de listener : PostHog SDK init avant plugins Nuxt → window.onerror PostHog gagne. Pas une régression mais un pattern d'architecture à connaître pour tout futur error tracker.

AtlasModéréConvention
15 mai 2026

Erreur : PDFKit gotchas découverts sur quote-pdf.ts (Q-31, devis Bilal 28 produits) : (1) doc.text(name, { width, ellipsis: true }) n'honore pas ellipsis sur fontSize 8 → texte wrap sur 2 lignes mais y += 12 fixe → chevauchements. (2) Colonnes mal calibrées : colSub 480 + width 70 = 550 > marge 545 → PDFKit ajoute des pages fantômes (devis 28 items sur 6 pages au lieu de 2).

Check ajouté : Mesurer doc.heightOfString(text, { width: colW }) AVANT chaque row et incrémenter y de max(rowHeight, measured + padding). Valider que colStart + colWidth <= pageWidth - margin pour CHAQUE colonne, sinon pages fantômes.

Cause racine : PDFKit ellipsis non fiable sur petites fontSizes + math layout naïve (y += constante) qui ignore le rendu réel. Overflow horizontal silencieux (pas d'exception, juste pages vides injectées).

claudeMajeurdebt_archi
15 mai 2026

Erreur : Depuis cutover PG 30/04, orders-db.ts l.419-422 stockait total_shipping_tax_incl = total_shipping_tax_excl en dur ("shipping HT-only, à ajuster" en commentaire). 1 vraie commande facturée sous-évaluée (Ambre #ACAI75NQEU, 16,67€ TVA manquante) + 5 commandes test Aude annulées. Bug masqué + manque de TVA shipping côté code (ne lisait pas tax_rules_group du carrier).

Check ajouté : Fix code orders-db.ts createOrderFromCart : fetch carrier.tax_rules_group × address.country + postcode → calcul shippingPriceTTC correct. Stocke total_shipping_tax_incl = TTC vrai. carrier_tax_rate stocké réellement. Fix collateral commit 5c124f81 + endpoint getCarriersFromDb (preview TTC checkout) + frontend affichage HT/TTC explicite (commit 95e91c93).

Cause racine : Migration MariaDB→PG du 30/04 a porté la création d'order sans implémenter la résolution tax shipping. La rule TRG 6 (carrier 20%) existait côté DB mais jamais lue. Cumul avec id_country=242 Corse qui n'avait aucune rule TVA shipping.

claudeModérédebt_archi
15 mai 2026

Erreur : Audit fiscal a révélé : (1) "France Corse" en pays séparé (id_country=242) + Réunion/Mayotte/Guyane/Saint-Pierre idem = patchwork PS hérité illégitime juridiquement. (2) 22 clients BE + 3 DE + 13 RE facturés 0% TVA par défaut PS (aucune rule configurée). (3) get­TaxRatesForProducts faisait MAX(rate) sans filtre postcode.

Check ajouté : Doctrine PaaS souverain Europe OSS établie 2026-05-15 : France=1 seul id_country, détection Corse + DOM-TOM par postcode (ps_tax_rule.zipcode_from/to). UE = ps_country.need_identification_number=1 → VAT intracom obligatoire B2B (cas Palimex). Migrations DB : 35 adresses 242/174/143/238/181→8, désactivation des 5 pays, création TVA 8.5% + rules postcode DOM, ajout cs_vat_intracom_cache pour VIES, vat_intracom sur customer_extra. Code : résolution postcode COALESCE(specifique, générique) + autoliq B2B 0% + export hors UE 0% + mention légale auto art. 262 ter I / 262 I CGI.

Cause racine : Config PS initiale Aude n'avait que les rules France métro (id_country=8). Pays UE/DOM/Corse activés en dur sans rules. Code v2 hérité de MariaDB sans pivot postcode-aware.

claudeMineurdebt_code
15 mai 2026

Erreur : Triplé bugs admin notif/PDF post-déploiement carrier+commentaire+TVA : (a) SQL fetchait cl.name sur ps_carrier_lang qui n'a que delay→ bloc Transporteur vide depuis le déploiement. (b) backslashes \" \n littéraux issus addifyb2bregistrationformbuilder pSQL non désescapés au rendu. (c) Intl.NumberFormat fr-FR utilise narrow NBSP (U+202F) non supporté par Helvetica builtin pdfkit→ "1 /276,15 €" au lieu de "1 276,15 €". (d) pdfkit.dash(0) crash. (e) drift Drizzle PG : ALTER omis sur ac_postgres + smoke_v2_postgres au 1er ship Sprint A.

Check ajouté : Carrier SQL : SELECT c.name (et non cl.name). Helper unescapeDbText centralisé (core/server/utils/db-text-unescape.ts) appliqué dans buildAddressBlockHtml + buildAdminNewOrderVars + PDF. stripUnsupportedSpaces dans formatPrice PDF. dash(0) wrapped en save/restore conditionnel. Doctrine drift = propager ALTER sur les 3 clusters PG (ac/palimex_v2/smoke_v2).

Cause racine : Bugs introduits/découverts pendant le sprint TVA EU OSS. Tous fixés dans la même session.

claudeMajeurdebt_code
15 mai 2026

Erreur : 7138/8049 (89%) anciens clients legacy PS 8.2 ne pouvaient pas se logger sur Palimex v2. Root cause double : 1. Module B2B addifyb2bregistrationformbuilder hashait passwd = md5(pSQL(_COOKIE_KEY_ . $pwd)) au lieu du standard core PS md5(_COOKIE_KEY_ . $pwd). Tout password avec apostrophe/quote/backslash/newline/HTML-tag ne matchait plus côté v2. 2. ecosystem.preprod.config.cjs n avait pas PS_COOKIE_KEY_PALIMEX_V2 — preprod renvoyait 401 sur 100% des MD5 (prod OK car .env présent).

Check ajouté : core/server/api/catalogue/customer/login.post.ts : verifyPassword teste 3 recettes en cascade (bcrypt → MD5 standard → MD5 pSQL escape) + rehashToBcrypt automatique en $2b$10$ après chaque MD5 réussi (migration progressive 7138 → 0). Commit f34b0b1a. Sentinelles MD5/pSQL validées T1-T4 sur preprod puis prod 2026-05-15.

Cause racine : Migration v2 (Nuxt) a reproduit la recette PS core mais ignoré la variante pSQL du module B2B legacy. Comme le legacy gérait inscription ET login avec la même recette pSQL custom, le bug est resté invisible jusqu au passage v2.

claudeCritiquesilent_err
14 mai 2026

Erreur : 12 commandes Nuxt v2 Palimex (depuis cutover 12/05) factures HT-only au lieu de TTC. Total_paid_tax_incl == total_paid_tax_excl, tax_rate=0 sur ps_order_detail. Conséquence : email client affiche Total TTC = Total HT, TVA jamais collectee (~300-370EUR perdu sur 6793EUR HT).

Check ajouté : Fix orders-db.ts: import getTaxRatesForProducts, resolve countryId+postcode depuis adresse livraison, calcul finalPriceTTC per item, taxRatio appliqué aux discounts. ps_order_detail.tax_rate persiste le taux par produit. Commit a4506e91 sur preprod.

Cause racine : createOrderFromCart() dans core/server/utils/orders-db.ts n appelait jamais getTaxRatesForProducts. Insert hardcode tax_rate=0 et total_paid = totalProductsHT + shippingPrice. cart-db.ts calcule la TVA correctement pour le panier mais la commande convertit en HT-only. Bug Day-1 du Nuxt v2, pas regression recente.

claudeMajeurrace_condition
14 mai 2026

Erreur : Footer Palimex v2 prod wipe via PUT /api/footer-config/sync (13/05 19:38 puis 14/05 07:23). Logo MEYVA, contact_email/phone, social, copyright tous NULL.

Check ajouté : loadIntoBuilder no-op si data.value?.footer falsy (pattern deja en place dans useHeaderDb.ts).

Cause racine : core/composables/useFooterDb.ts loadIntoBuilder() retombait sur {theme:dark} si useFetch /api/footer-config pas encore resolu. saveAll() dans BuilderSidebar.client.vue appelle syncFooterToDb() unconditional. Race: clic Enregistrer avant fetch -> push vide -> upsert wipe.

claudeModéréaudit-method
14 mai 2026

Erreur : Audit massif curl 25-50 parallèles sur palimex-fruits-secs.com → nginx silent-rate-limit renvoie 404 spurious. Diagnostic erroné "60% du site mort" pendant ~2h alors que tout allait bien. Re-test séquentiel calme : 20/20 URLs marquées 404 répondent en fait 200.

Check ajouté : Toute audit > 50 URLs sur tenant prod : UA browser obligatoire + concurrence ≤5 + throttle 0.3s. Cibles testées séquentiellement = source de vérité ; audits parallèles servent uniquement à dégrossir. Cf memory feedback_curl_audit_rate_limit.md

Cause racine : Default UA "PalimexAudit/1.0" déclenchait bot-shield ; 25 jobs parallèles + follow redirect saturait nginx → 404 silent au lieu de 429.

claude-codeMajeursilent_err
14 mai 2026

Erreur : /api/hub-tenant/posthog/recordings retourne 401 sur mothership malgre TENANT_PROXY_SECRET_PALIMEX_V2 present dans .env du host. Symptome cote palimex-v2 : Mothership proxy: Server Error sur /hub/bi/posthog. Env vars nouvelles invisibles dans le container ac_nuxt.

Check ajouté : deploy-nuxt.sh switche de docker start a docker compose -f COMPOSE_FILE up -d --no-deps --force-recreate. Commit 4a3be07e. Fallback docker start preserve.

Cause racine : docker start reutilise les env vars GELEES a la creation du container. env_file: .env du docker-compose.yml n est lu QUE par docker compose up, pas par docker start/restart. deploy-nuxt.sh ligne 278 faisait docker start sans recreate.

claude-codeMajeursilent_err
14 mai 2026

Erreur : /hub/bi/posthog build echoue : Cannot find module @tailwindcss/typography au require dans core/tailwind.config.ts. Build incremental + retry purge cache KO tous les deux. Aucun deploy AC depuis le 12/05 (commit 0b7fcc2f qui a introduit le dep) -> bug latent revele aujourd hui par ./deploy ac.

Check ajouté : Pre-commit a ajouter : grep des require()/import depuis core/*.config.ts et verifier que chaque dep soit en dependencies (pas devDependencies) dans package.json racine.

Cause racine : Container ac_nuxt + preprod_nuxt ont NODE_ENV=production donc npm install skip les devDependencies. Les packages tailwindcss + @tailwindcss/typography + rolldown-vite (overrides) etaient en devDependencies alors qu ils sont consommes au BUILD de production. Fix : git mv 3 packages devDeps -> deps + regen package-lock (commit f9a2f553).

Verified reviews

Nos clients parlent de nous

5.0 / 5

33 client reviews

Nous travaillons avec Alexandre depuis quelques mois et sommes ravis de son a

Nous travaillons avec Alexandre depuis quelques mois et sommes ravis de son accompagnement. Alexandre comprend parfaitement les enjeux et problématiques liés à notre activité et parvient, en réponse, à nous apporter des solutions techniques et fonctionnelles en respectant coûts et délais.

Office

MONOGRAM

Malt

La résolution de mes problèmes à été rapide et efficace, je recommande :)

La résolution de mes problèmes à été rapide et efficace, je recommande :).

Marine

MES-Distribution

Malt

Super

Super

Jl

Kaigan

Malt

Au Top

Au Top. tout simplement

Elite Cbd

Canna Elite Europe Ltd

Malt

Configuration d''un VPS et migration réalisée avec succès, bons conseils, dia

Configuration d'un VPS et migration réalisée avec succès, bons conseils, diagnostique rapide et efficace de nos problèmes. Je recommande.

Lorie

GRIIN outdoor

Malt

Toujours aussi clair et clairvoyant

Toujours aussi clair et clairvoyant... ;) Un plaisir de travailler avec Alexandre

Elite Cbd

Canna Elite Europe Ltd

Malt