# Debug & post-mortems Ce fichier sert à capitaliser sur les problèmes rencontrés. ## À documenter - bug pénible - mauvaise compréhension - fausse hypothèse - solution finale ## Objectif Ne plus jamais perdre du temps sur le même problème. --- # Post‑mortems ## SQL Server qui crash dans un conteneur LXC Proxmox ### Contexte NUC personnel sous Proxmox avec plusieurs services en conteneurs LXC. Un conteneur SQL Server (Microsoft SQL Server Linux) ne démarrait plus. ### Symptômes - `sqlcmd` impossible → timeout - service `mssql-server` en boucle de restart - logs contenant : ``` Operation not permitted chmod: changing permissions of '/var/opt/mssql/log/...' ``` - crash + génération de core dump ### Cause probable SQL Server utilise certaines opérations système qui sont mal supportées dans les conteneurs LXC (permissions, filesystem, capabilities). Dans un environnement Proxmox LXC, cela peut casser après : - une mise à jour - un changement de permissions - un changement de configuration du conteneur ### Conclusion SQL Server **n'est pas un bon candidat pour un conteneur LXC Proxmox**. ### Décision architecturale Pour un homelab ou un petit serveur : - éviter SQL Server en LXC - préférer : - PostgreSQL - MariaDB / MySQL Si SQL Server est nécessaire : - utiliser une **VM complète** plutôt qu'un conteneur. ### Règle à retenir > Éviter les bases lourdes nécessitant des capabilities système avancées dans des conteneurs LXC. --- ## Suppression silencieuse due à deux éditions concurrentes sur le même fichier ### Contexte Un même fichier a été modifié par deux mécanismes proches dans le temps : édition en cours d’agent et passe outillée/linter/formatteur. ### Symptômes - bloc de code disparu sans erreur explicite - diff final incohérent avec l’intention de modification - impression de “régression fantôme” après une édition pourtant correcte ### Cause probable Deux processus ont réécrit le même fichier sans coordination, le second écrasant silencieusement une partie du travail du premier. ### Correctif / règle à retenir - éviter deux passes d’écriture concurrentes sur le même fichier - relire le diff immédiatement après toute passe automatique - privilégier une séquence stricte : édition, puis lint/format, puis vérification --- ## tsx + NestJS : injection par type cassée silencieusement ### Contexte Projet `app-alexandrie`, Epic 3, le 10-03-2026. Le backend NestJS tournait avec `tsx watch` dans un contexte ESM (`module: nodenext`), notamment pour rester compatible avec Prisma v7. ### Symptômes - `TypeError: Cannot read properties of undefined (reading 'get')` dans le constructeur d’un service - `ConfigService` injecté par type mais `undefined` au runtime - `@Injectable()` et `ConfigModule` correctement configurés, sans erreur de compilation ### Cause probable `tsx` repose sur esbuild pour transpiler TypeScript. Dans ce contexte, `emitDecoratorMetadata` est ignoré même s’il est activé dans `tsconfig.json`. NestJS ne peut donc plus résoudre correctement certaines injections par type, notamment `constructor(private readonly config: ConfigService)`. ### Correctif / règle à retenir - ne pas supposer que `emitDecoratorMetadata` fonctionne avec `tsx` - dans ce contexte, éviter l’injection par type de `ConfigService` pour les services d’infra - lire explicitement les variables via `process.env`, après chargement amont de `ConfigModule.forRoot()` Exemple : ```typescript // AVANT constructor(private readonly config: ConfigService) { const host = this.config.get('REDIS_HOST'); } // APRES constructor() { const host = process.env['REDIS_HOST'] ?? 'localhost'; } ``` ### Alternative écartée `nest start --watch` a été testé mais a introduit des conflits ESM/CJS dans ce contexte (`exports is not defined`). --- ## `export { fn }` ne constitue pas un import local — détecté uniquement au build ### Contexte Projet `app-template-resto`, story 2-4, le 17-03-2026. Dans `getPublicHomeData.ts`, la fonction `resolvePublicTenantSelection` avait été déplacée dans `src/server/tenant/resolvePublicTenant.ts` et re-exportée depuis l'ancien emplacement. ### Symptômes - `Cannot find name 'resolvePublicTenantSelection'` au `next build` uniquement - ESLint et `tsc` (hors build) ne signalaient rien - La fonction était utilisée localement dans le même fichier qui la re-exportait ### Cause ```typescript // getPublicHomeData.ts export { resolvePublicTenantSelection } from "@/server/tenant/resolvePublicTenant"; // puis, plus bas dans le même fichier : const result = resolvePublicTenantSelection(env); // ← NameError au build ``` Un re-export (`export { fn } from "..."`) ne crée pas de binding local dans le fichier. La fonction est exportée vers l'extérieur mais n'est pas disponible comme variable locale. ### Correctif / règle à retenir Si une fonction est utilisée dans le même fichier qui la re-exporte, ajouter un `import` séparé en plus du `export` : ```typescript import { resolvePublicTenantSelection } from "@/server/tenant/resolvePublicTenant"; export { resolvePublicTenantSelection }; // pour les appelants externes ``` --- ## CLI npm globale qui ne se met pas à jour (prefix / permissions / contexte projet) ### Contexte Mise à jour de `@openai/codex` via la CLI (`codex update`), sur une machine avec installation npm globale utilisateur (`~/.npm-global`) et exécution depuis un repo contenant un `.npmrc` non standard. ### Symptômes - Message d’update CLI affiché mais version inchangée après `npm install -g` - `codex --version` reste sur une ancienne version - Installation via `sudo` ne change rien - `which codex` et `npm root -g` pointent vers des chemins différents ### Cause - Décalage entre : - le **prefix npm** utilisé pour installer - le **binaire exécuté** - Ancienne installation toujours active dans le bon prefix utilisateur - Contexte projet (`.npmrc`) pouvant influencer le comportement de npm ### Correctif / règle à retenir - Ne jamais utiliser `sudo npm install -g` - S’assurer que : - `npm config get prefix` = dossier utilisateur (ex : `~/.npm-global`) - `which ` pointe vers ce même prefix - Faire les installs globales hors d’un repo (éviter `.npmrc` projet) - En cas de doute, nettoyer : ```bash rm -rf ~/.npm-global/lib/node_modules/ rm -f ~/.npm-global/bin/ npm install -g @latest ``` ### Commandes de diagnostic utiles - `npm config get prefix` - `which ` - `npm root -g` - `npm ls -g --depth=0 ` | npm list -g @openai/codex --depth=0 - --version --- ## Sub-agents Claude Code — `Write` indisponible dans la sandbox `Explore` ### Contexte Workflow BMAD `testarch-test-review` sur RL799_V2 (24-04-2026) utilisant 4 sub-agents `subagent_type=Explore` pour évaluer 4 dimensions qualité en parallèle. Chaque sub-agent devait écrire un fichier JSON dans `/tmp/`. ### Symptômes - Les 4 sub-agents ont terminé leur analyse avec succès mais **aucun n'a réussi à écrire son fichier JSON** - Messages de retour : *"Je rencontre une limitation d'outillage… je suis en mode READ-ONLY… je génère le rapport directement en texte."* ### Cause Le sub-agent type `Explore` n'a pas accès à l'outil `Write` dans sa sandbox (spec : "Tools: All tools except Agent, ExitPlanMode, Edit, Write, NotebookEdit"). Non documenté clairement dans les workflows TEA qui demandent pourtant d'écrire en JSON. ### Correctif / règle à retenir 1. **Ne pas demander aux sub-agents `Explore` d'utiliser `Write`** — briefer explicitement "retourne le JSON en bloc dans ta réponse finale" 2. **L'orchestrateur matérialise** les fichiers de sortie pour le compte des sub-agents 3. **Alternative** : utiliser `subagent_type=general-purpose` qui a accès à tous les tools (mais plus cher en tokens et moins spécialisé pour l'exploration) Extrait de brief corrigé pour futur usage : ``` Ta mission : analyse X dans les fichiers Y. Format de sortie : JSON structuré selon le schéma ci-dessous. IMPORTANT : retourne le JSON directement dans ta réponse finale, entre blocs ```json```. Ne tente pas d'écrire de fichier (Write indisponible dans ta sandbox). L'orchestrateur matérialisera le fichier à partir de ton retour. ``` --- ## Effet iceberg en CI — patcher en cascade jusqu'au fond du puits ### Contexte Quand un fix CI structurant rétablit un pipeline qui foirait depuis longtemps, **plusieurs bugs latents en aval peuvent apparaître en cascade** : ils étaient tous présents avant, juste invisibles parce que le runner s'arrêtait à l'échec amont. Vécu sur RL799_V2 le 30-04 / 01-05-2026, 8 étages d'iceberg fixés en cascade. ### Symptômes | # | Phase | Symptôme | Cause | Fix | |---|---|---|---|---| | 1 | CI tests | `Cannot find module '@org/shared'` | `dist/lib` non bâti avant `test:api` | Build workspace en amont | | 2 | CI tests | `Module '@prisma/client' has no exported member 'X'` | Client Prisma non généré | Inverser `prisma generate` → `pnpm build` | | 3 | CI tests | `Seed incomplet : 0 users / N attendus` | Étape seed manquante | Ajouter `prisma db seed` après `prisma migrate deploy` | | 4 | CI tests | ` non configuré (requis hors dev)` | Variable d'env applicative manquante en CI | Définir au bloc `env:` du job | | 5 | CI tests | 14×500 sur endpoints qui chiffrent | `ENCRYPTION_KEY` manquante | Idem | | 6 | CI tests (PDF) | `Could not find Chrome` | Puppeteer cherche son cache local absent du runner | `PUPPETEER_EXECUTABLE_PATH=/usr/bin/google-chrome-stable` | | 7 | CD prod (migrate) | `Cannot find module '/app/scripts/check-node-version.mjs'` | `pnpm run prisma:migrate` appelle un script absent de l'image API | Appel direct du binaire Prisma | | 8 | CI tests | Test attend `50,00 €` reçoit `1,19 €` | `waitForNotification` mal scopé (filtre par `type` mais pas par `recipientId`) — masquée par les étages 1-7 | Re-run OU patch chirurgical du `where:` | Chaque étage masquait le suivant. Aucun n'était nouveau — tous présents avant la session, mais invisibles à cause des étages amont. ### Cause - **Local ≠ CI** : en local, `dist/` traîne, le client Prisma est généré, la DB est seedée d'une session précédente, le `.env` est complet. Le bug est invisible - **Pipeline early-exit** : un échec à l'étape N ne laisse rien tourner aux étapes N+1, N+2, … - **Effet additif des sessions** : plus le pipeline est cassé depuis longtemps, plus le code applicatif a évolué sans validation CI ### Correctif / règle à retenir 1. **Validation locale stricte avant push CI structurant** : simuler les conditions CI vierges (`rm -rf node_modules/.prisma packages/*/dist apps/*/.next` + relancer la chaîne complète) 2. **Lecture honnête des nouveaux failures** : après un fix CI structurant, ne pas présumer que les nouveaux failures sont des régressions du fix. Probablement des bugs latents 3. **Tableau iceberg** : noter au fil de la session le tableau (étage / symptôme / cause / fix). Ne pas se laisser submerger par "ça casse encore" 4. **Push après chaque étage** : ne pas attendre d'avoir tout fixé. Chaque fix structurant mérite son commit thématique 5. **Ne pas stopper trop tôt** : un seul push ne révèle qu'un étage. Tant qu'il y a des bugs latents, le pipeline cassera ### Signal pour repérer un effet iceberg - Le pipeline était cassé depuis ≥ 1 semaine - Le fix d'aujourd'hui touche une étape **précoce** du workflow (install, build, generate, migrate) - Les commits récents ont ajouté des features sans valider en CI - Sentiment vague de "ça pourrait casser plein d'autres trucs" — c'est probablement vrai --- ## Prisma migrate inclut les diffs cosmétiques (RenameIndex) ### Contexte `prisma migrate dev --create-only --name add_lodge_settings` peut générer une migration qui contient (1) le changement attendu mais aussi (2) un side-effect cosmétique pré-existant entre le schema Prisma et la DB qui n'avait jamais été nettoyé. RL799_V2 — migration `20260427120920_add_lodge_settings` qui ramassait un `ALTER INDEX … RENAME TO …` orphelin. ### Symptômes - Migration thématique qui contient un rename d'index sans rapport avec le scope de la story - Un dev qui regarde la migration ne comprend pas pourquoi cet `ALTER INDEX` est là ### Options et décision | Option | Pro | Con | |---|---|---| | Garder le rename dans la migration thématique avec commentaire | la prochaine `prisma migrate dev` ne re-générera pas ce rename | le commit "thématique" contient un side-effect cosmétique | | Retirer le rename | commit propre | la prochaine migration thématique l'inclura à nouveau → piège pour le prochain dev | | Migration de cleanup séparée | plus propre | nécessite 2 migrations + 2 PRs | **Décision recommandée** : option 1 avec commentaire explicite dans le `.sql` : ```sql -- RenameIndex (réalignement DB ↔ schema, dérive cosmétique pré-existante) ALTER INDEX "tronc_entries_tenue_idx" RENAME TO "tronc_entries_tenue_id_idx"; ``` ### Correctif / règle à retenir - **Préventif** : `prisma migrate diff` régulièrement (CI/CD ou pré-commit) pour détecter la dérive AVANT qu'elle ne pollue une migration thématique - **Curatif** : inspecter manuellement le SQL généré par `--create-only` avant de l'appliquer en migration thématique