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 2026Erreur : 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.
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
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
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.
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.
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.
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.
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.
TuringModéréentity_array_serialize 17 mai 2026Erreur : 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 {...})
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)
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
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.
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.