Conversation
…que des lignes (TASK-1615) `loop_messages.created_at` est un `timestamp` de precision 0 — la seconde pleine, sans sous-seconde. Des messages ecrits dans la meme requete portent donc EXACTEMENT la meme valeur, et la base locale compte deja 8 groupes (loop_id, created_at) reellement a egalite. `LoopClaimCompiler::sourceMessages()` ordonnait par `created_at` SEUL. A egalite, PostgreSQL rend un ordre indefini, libre de varier entre deux requetes identiques selon l'etat du tas. Or `empreinteSource()` hache l'ORDRE : deux lectures d'une source INCHANGEE rendaient deux empreintes. La garde de cout de TASK-1541 cedait alors — `hash_equals` echouait, le compilateur resolvait un provider et appelait le modele. Le balayeur automatique passe toutes les dix minutes : une conversation ENDORMIE pouvait etre refacturee a chaque passage. Defaut de production, pas flake de test. `(created_at, id)` est l'ordre canonique deja documente par ClaimResurrectionGuard et applique par GuestConversation. sourceMessages() etait le lecteur qui ne l'appliquait pas. MESURE — le test echoue AVANT la correction (PostgreSQL) : ✓ premisse : les 3 messages portent bien le meme created_at ⨯ la requete de source porte un ordre TOTAL ⨯ l empreinte ne bouge pas quand l ordre physique des lignes change ✓ l ordre rendu est stable d un appel a l autre Apres : 4/4 verts ; TASK1541ClaimRetrievalTest 9/9 ; Knowledge + Architecture 573 verts ; et le shard Feature 4/6 EXACT qui avait rougi deux fois en CI rejoue OK (1355 tests, 6424 assertions). Decouvert par la CI de la PR #615 : TASK-1614 n'ajoute qu'un fichier de tests, mais celui-ci redistribue les six shards et remue la table autrement. La branche a EXPOSE le defaut, elle ne l'a pas cause. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…615) Second cas de la MEME famille, expose par la redistribution des shards. CI de la PR #616 : shard 4/6 VERT (le defaut vise est ferme), mais shard 1/6 rouge sur TASK1096SeriesNumberingTest::test_numbering_is_never_persisted : - 0 => 'La racine' + 0 => 'Une annexe' - 1 => 'Une annexe' + 1 => 'La racine' `blog_posts.created_at` est AUSSI un timestamp de precision 0. Les deux articles sont ecrits dans la meme requete, donc a la meme seconde : a egalite `created_at` seul ne departage rien et PostgreSQL rend un ordre indefini. Le test asserait donc un ordre que la requete ne garantissait pas. Le defaut est dans le TEST, pas dans le code metier. Ajouter le departage ne l'affaiblit pas : il lui fait asserter quelque chose de DEFINI. Perimetre STRICTEMENT borne par arbitrage : cette ligne seule. Les 66 autres occurrences de orderBy('created_at') dans tests/ ne sont ni auditees ni corrigees ici. Rejeu PostgreSQL local : shard 1/6 OK (1299 tests), shard 3/6 OK (1462 tests, ou le fichier a migre apres l'edition), shard 4/6 OK (1331 tests). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…ssage-pour-l-empreinte-source-de-loopclaimcompiler TASK-1615 — Ordre TOTAL des LoopMessage pour l'empreinte source de LoopClaimCompiler
…zation (TASK-1614)
SLICE A de la Product Spec V0 « ChatLoop — 3 assistants IA », validee
repo-grounded par OPUS et passee a VALIDATED sur cette branche.
Ce que cette TASK rend possible, et rien de plus :
« ce plugin existe au catalogue plateforme, et il est autorise ou
non pour telle Organization. »
Le CATALOGUE est un fichier (config/loop_plugins.php), pas une table :
un deuxieme plugin doit rester une entree de configuration, comme un
cinquieme type de Boucle l'est dans config/loop_types.php.
La DISPONIBILITE est une table (organization_loop_plugins), parce que
c'est une decision, qu'elle change sans deploiement et qu'elle est
toujours celle d'UNE Organization. organization_id y est NOT NULL :
loop_type_settings l'accepte nullable parce qu'un reglage de type existe
a deux portees et que null y veut dire « la Plateforme » ; ici un null
voudrait dire « disponible partout », soit le contraire de ce qu'une
capacite experimentale doit faire quand personne n'a rien decide.
Ferme par defaut. Eteindre n'efface pas la ligne : available=false
conserve qui a coupe et quand.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…tre du cycle de vie (TASK-1614) CI rouge, shard Feature 3/6 : UserDataLifecycleRegistryTest::test_every_user_foreign_key_is_declared_in_lifecycle_registry -> 'Every FK to users.id must be classified in UserDataLifecycleRegistry.' La migration ajoute une cle etrangere vers users.id (updated_by, qui porte l'auteur de la decision de disponibilite). Le depot tient un registre DECLARATIF de toutes ces cles : une cle non classee est une donnee utilisateur dont personne n'a dit ce qu'elle devient a la suppression du compte. La garde a fait exactement son travail. Politique DETACH, org_scope direct, sur le modele de custom_loop_types.created_by et ai_credit_setting_changes.changed_by : savoir si un plugin est autorise dans une Organization est une decision d'administration, pas une donnee personnelle. Elle doit survivre a son auteur, qui est simplement detache — la cle est deja nullOnDelete. Mesure : 30 verts en PostgreSQL (UserDataLifecycle + TASK1614). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…istants-ia-disponibilite-organization TASK-1614 — Catalogue plugin « 3 assistants IA » + disponibilité Organization
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…tants se reglent (TASK-1616)
SLICE B de la Product Spec V0 « ChatLoop — 3 assistants IA ». Les deux
maillons suivants de la chaine posee par SLICE A :
catalogue plateforme -> disponibilite Organization
-> ACTIVATION BOUCLE -> CONFIGURATION DES 3 ASSISTANTS
LE PLUGIN N'EST PAS UNE LOOPCARD, et c'est la decision centrale.
Le bloc « Actions de ChatLoop » est pilote par les Cards de placement
chat_action (« Resume IA »). Modeliser le plugin ainsi aurait semble naturel
et aurait ete faux : LoopPresetConfigurator::canConfigure() est
proprietaire-only — « un animateur anime, il ne recompose pas » — et la
matrice mesuree donne loops.manage_cards = NON pour owner, facilitator ET
member, TASK-1083 l'ayant retiree au motif qu'elle annoncait un droit que
l'interface ne donnait pas.
Passer par la Card imposait donc de choisir entre exclure le facilitator
(contre le CDC §5) et percer une doctrine posee expres. Le plugin garde son
PROPRE etat, rendu A COTE de « Resume IA », et son PROPRE droit :
loop_plugins.configure — owner + facilitator, membre refuse, SuperAdmin et
Admin d'Organization par les etapes 3 et 4 du resolveur.
LE HARD GATE VIT DANS LE SERVICE, pas dans l'ecran. Cacher un bouton n'a
jamais empeche un POST : chaque lecture et chaque ecriture reconfronte la
disponibilite de l'Organization de la BOUCLE. Retirer l'autorisation eteint
toutes ses Boucles sans reecrire une ligne.
ECART, PAS COPIE. Une posture identique au defaut n'est pas stockee, et vider
un champ SUPPRIME l'ecart — sinon la Boucle figerait le texte du jour. Les
postures SURVIVENT a l'extinction, dans la Boucle comme dans l'Organization.
ZERO APPEL PROVIDER : ni capability, ni AiEconomicGuard, ni ai_pricing, ni
OpenRouter, ni AiTurnLock, ni Evidence Build, ni follow-up.
MESURES
27/27 verts. Sabotage 1 (gate Organization neutralise) -> 6 rouges ;
sabotage 2 (droit retire au facilitator) -> 3 rouges. Voisins : 238 verts.
Recette navigateur : les deux surfaces, l'ecran des trois postures, la
persistance relue, et 403 en URL directe sur une Organization non autorisee.
UX_DEBT_SLICE_E : le facilitator detient le droit mais n'atteint pas /outils,
garde par canConfigure() des Cards, anterieur a cette TASK. Arbitrage de
Cyril : ne pas ouvrir cette page, reporter la decouvrabilite a la SLICE E sur
une surface accessible depuis ChatLoop. Consigne comme dette, pas comme bug.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…-assistants-ia-dans-une-boucle-et-configuration-aperio-traverse-limen TASK-1616 — Activation du plugin « 3 assistants IA » dans une Boucle + configuration Aperio/Traverse/Limen
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…nomie (TASK-1617)
SLICE C de la Product Spec V0. Le SuperAdmin choisit un modele OpenRouter
VERIFIE GRATUIT par assistant, la capability loop_multi_ai existe, et un
modele prouve gratuit vaut un cout CONNU de 0. Zero generation.
LE SERVICEPROVIDER DE FUSION A ETE ABANDONNE, et c'est la decision centrale.
Fusionner les slugs gratuits dans config('ai_pricing.models.openrouter') au
boot semblait le chemin court. Deux raisons l'ont tue, la seconde dirimante :
lire la base au boot rend `artisan migrate` dependant d'une table metier ; et
surtout `php artisan config:cache` SERIALISE la configuration dans un fichier
— la fusion y serait FIGEE, donc une preuve de gratuite perimee servie
indefiniment, exactement l'inverse de la fraicheur de 15 minutes exigee.
A la place : DynamicPricingSource, resolue A L'APPEL, consultee seulement la
ou la configuration versionnee n'a rien dit. Elle ne calcule aucun cout : elle
propose une entree qu'AiPricingCatalog valide comme les autres. UNE autorite
de prix, UN ledger. `bind` et non `singleton` : rien n'est lu au boot.
LE CHIFFRAGE NE SORT JAMAIS SUR LE RESEAU. entryFor() lit exclusivement la
preuve persistee. Rafraichir appartient a LoopPluginModelGuard, sur le chemin
de la generation — la ou un echec est un refus, pas une trace perdue.
LA GARDE EST INDEPENDANTE DU CATALOGUE DE PRIX. Savoir COMBIEN ca coute
n'autorise pas a APPELER : un tarif statique historique ne doit pas laisser
generer un modele dont la preuve FREE a expire.
La gratuite se PROUVE sur les tarifs : prompt, completion ET request a 0,
sortie texte exclusivement, tout poste applicable non explicitement ignore
doit valoir 0. Le suffixe « :free » n'a aucune autorite.
DEUX DEFAUTS TROUVES PAR LA RECETTE, pas par les tests :
1. « openrouter/free » etait selectionnable. Il est bien prouve gratuit, et
refuse quand meme : ce routeur choisit lui-meme son modele, donc la trace
nommerait le routeur plutot que le modele employe (CDC section 3).
2. timestampTz rendait TOUTE preuve perimee, en PostgreSQL seulement. La
valeur revenait dans le fuseau de la session (+02:00 en CEST) face a un
Carbon::now() en UTC : une preuve de 29 secondes se lisait 2 h dans le
passe. La garde aurait refuse toute generation pendant l'heure d'ete.
SQLite ignore les fuseaux — les 34 tests passaient.
ECONOMIE : loop_multi_ai entre dans LEDGER_AUTHORITY_SINCE_BY_PROCESS (garde,
16 process) et PAS dans CREDITABLE_PROCESSES (credit, 14, inchange). Deux
semantiques, jamais fusionnees. Les deux recensements voisins (T1286, T1261)
ont rougi et ont ete mis a jour avec leur raison — c'est leur role.
ProviderResolver INCHANGE. Aucun BYOK. Contrat credential pour SLICE D
consigne dans le TASK file.
MESURES : 35 verts en SQLite ET en PostgreSQL ; 3 sabotages (TTL, reseau dans
le chiffrage, retour a timestampTz) ; 269 voisins verts ; recette sur le
catalogue REEL (446 modeles, 21 selectionnables, trois modeles differents
affectes et Operationnels).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…s (TASK-1617)
CI rouge au premier passage : six echecs, deux familles, aucune regression
fonctionnelle — ce sont toutes des gardes de DECLARATION.
1. UserDataLifecycleRegistryTest : loop_plugin_ai_models.updated_by n'etait
pas au registre du cycle de vie. Politique DETACH, org_scope none (la
table est PLATEFORME, sans organization_id).
TROISIEME fois que cette garde parle dans la campagne, DEUXIEME oubli :
TASK-1614 l'a enseignee, TASK-1616 l'a appliquee dans la meme salve,
TASK-1617 l'a re-oubliee.
2. Cinq recensements de capabilities (T1227, T1253 x2, T1284, T1285).
Ajouter une capability change trois choses que le depot fige exprès :
- son LIBELLE HUMAIN doit exister dans les deux locales. loop_multi_ai
n'en avait aucun — vrai manque : sans lui, la capability serait partie
en production affichee comme une cle technique ;
- la liste ORDONNEE des canoniques ;
- les comptes : couvertes 15 -> 16, total 19 -> 20.
Mis a jour avec leur raison. Ces gardes ont eu raison.
Mesure : 150 verts en PostgreSQL sur toute la famille touchee, 53 en SQLite.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…uter-free-capability-loop-multi-ai-et-economie-ia TASK-1617 — SLICE C : modèles OpenRouter free, capability loop_multi_ai, économie IA
Evidence Build unique via ContextBuilder, modele effectif par assistant, ledger feature loop_multi_ai:<assistant>, traces evidence_shared. 39 tests verts. Point de restauration AVANT la salve de sabotage. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…la partagent (TASK-1618) SLICE D de la campagne ChatLoop « 3 assistants IA ». Une question, des preuves construites UNE FOIS par ContextBuilder, trois generations sequentielles Aperio -> Traverse -> Limen qui les partagent, et ce qu'il en reste ecrit. Aucune UX : ce lot rend une structure, SLICE E decidera de l'affichage. Trois invariants tenus, et mesures : 1. l'empreinte de SharedEvidence decide SI les preuves peuvent etre partagees. Elle ne remplace aucun controle : appartenance active, meme Organization, disponibilite du plugin et refus de source restent AVANT elle ; 2. un refus avant le provider n'ecrit aucune AiProviderInvocation mais laisse une trace metier nommee (assistant_key, turn_id, correlation_id, raison) ; 3. un assistant en ERROR ou REFUSED ne bloque pas les suivants. Le moteur documentaire n'est JAMAIS appele : answer() GENERE, et l'appeler pour ses preuves aurait paye une quatrieme generation hors ledger du plugin, sur le modele de l'Organization. Le modele effectif garde le provider, l'instance SDK et donc le credential du tenant ; seul le slug bascule sur le modele prouve gratuit (TASK-1617). Le ledger porte feature = loop_multi_ai:<assistant>, capability canonique. 40 tests (SQLite + PostgreSQL), 7 sabotages mesures. L'un d'eux est reste vert et a revele un trou de couverture reel : le filet de securite externe n'etait teste par personne. Un test a ete ajoute, le sabotage rougit desormais. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…ge-orchestration-sequentielle-3-assistants-et-traces TASK-1618 — SLICE D : l'Evidence se construit une fois et les 3 assistants la partagent
… seul, publieur, synthese (TASK-1619) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…pChat (TASK-1619) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…ulle (TASK-1619) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…elles produit (TASK-1619) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…te responsive reel (TASK-1619) tests/e2e/helpers/auth.js n'a JAMAIS ete commite : git log --all ne rend rien. Le shim TASK-1317 dans ai/ (gitignore) pointait donc dans le vide, et TOUT le banc e2e etait inexecutable — « Cannot find module » sur n'importe quel spec. Le gate RESPONSIVE_HARD_GATE_SLICE_E utilise un VRAI viewport Playwright (test.use), pas un redimensionnement MCP : 1614, 1616 et 1617 avaient annonce « responsive verifie » sur une mesure qui restait a 1920. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
Quatre boutons (Aperio / Traverse / Limen / Demander aux 3), etats individuels, traduction produit du 429, reponses partielles conservees, reessai HUMAIN, follow-ups du meme appel, synthese Limen declenchee par l'humain, et la decouvrabilite du plugin pour le facilitator. Deux arbitrages MASTER tenus, et mesures : - un assistant en echec n'ecrit RIEN dans le fil. Les reussites sont des bulles permanentes ; l'indisponibilite vit dans le composeur, chez le seul demandeur. Le fil est lu par tout le cercle et pour toujours : un incident de trente secondes n'y a pas sa place ; - « Demander aux 3 » lance les trois EN PAIRS. Aucun ne lit les autres. La synthese est un GESTE humain, la seule lecture d'une IA par une IA du plugin. Aucun code technique n'atteint l'ecran : PROVIDER_CALL_FAILED, RATE_LIMITED, HTTP 429 et upstream_provider_shared_pool restent dans les traces SuperAdmin. `loop_chat.multi_ai` est declare comme 19e execution_path — etiqueter le plugin `loop_chat.ia` aurait refait la regression que V0-G avait corrigee. La recette reelle a trouve un defaut que le banc ne pouvait pas voir : un modele qui place la section « Pour approfondir » en tete vidait le corps et faisait jeter une reponse reellement obtenue et payee. Corrige et teste. 29 tests Livewire + 6 tests Playwright sur un VRAI viewport (mobile + desktop), fermant RESPONSIVE_HARD_GATE_SLICE_E. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…assistants-etats-partiels-429-produit-follow-ups-et-responsive TASK-1619 — SLICE E : les 3 assistants dans ChatLoop, états partiels et libellés produit
Defaut constate par Cyril sur test.laravel : cliquer « Demander a Aperio » lisait le composeur et GENERAIT aussitot. Le message humain n'avait pas ete soumis, et « reflechit… » pouvait apparaitre sur un texte que personne n'avait envoye — trois generations payees pour un brouillon. La cause etait une erreur de conception de TASK-1619 : j'avais fait des assistants des ACTIONS, la ou le composeur n'a jamais eu qu'un MODE et un declencheur, le submit. Le composeur n'a plus qu'une action : « Demander aux 3 IA ». Elle arme un mode EXCLUSIF (elle eteint IA et Dossiers : trois assistants en plus d'un moteur documentaire seraient quatre generations pour un envoi). Elle ne declenche rien. Le tour part de `sendMessage()`, au meme rang que les autres moteurs, apres publication du message humain — Entree comprise, puisqu'elle emprunte le meme handler. Le mode est ONE-SHOT, contrairement a IA et Dossiers : un membre qui a demande trois regards une fois n'a pas demande a en payer trois a chaque phrase. Le desarmement a lieu AVANT le tour, donc meme un echec ne laisse pas le mode arme. Deux consequences assumees : - le message humain reste dans le fil meme si les trois echouent. Il a ete ENVOYE : il appartient au membre. Seul l'echec reste hors du fil ; - « demander a une autre IA » est retire : dans le flux « aux 3 », les deux autres ont deja repondu, et c'etait un declencheur direct de plus. La configuration quitte le composeur pour « Gerer la Boucle » : configurer n'est pas un geste de conversation. Le droit et la route ne bougent pas. Badge admin : « Operationnel » devient « Gratuit verifie ». Il mesure modele choisi + encore au catalogue gratuit + preuve fraiche, jamais la disponibilite du provider — les recettes 1618 et 1619 ont montre le badge vert pendant que chaque appel rendait 429. Aucun health-check ajoute. 20 tests neufs dont celui qui aurait rougi (armer sans soumettre = zero generation, sabotage verifie), 27 tests SLICE E migres, 7 tests Playwright sur vrai viewport. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…eur-ne-genere-qu-au-submit-et-badge-admin-factuel TASK-1620 — le mode 3 IA s'arme, et seul le submit génère
…ups (TASK-1621) Les bancs TASK-1619 et TASK-1620 sont RETIRES : ils pilotaient des actions et une Evidence qui n'existent plus. Leurs garanties encore valides sont reprises par TASK1621PourContreEngineTest (moteur) et le banc de flux a venir. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…ent de preuve (TASK-1621) Le message humain est publie et RENDU avant toute generation : le submit remplit une file, une requete differee (wire:init, precedent du depot) lance un role a la fois. POUR s'affiche sans attendre CONTRE. Deux retours de recette de Cyril, corriges et testes : - une pastille de mode VIDE doublait le badge. Le composeur ne connait pas le mode `multi_ai` et rendait une puce sans libelle, reduite a son bouton de fermeture. Un seul badge desormais, celui du panneau d'etat ; - le badge d'attente portait un sablier sans identite : il porte l'icone du selecteur a gauche, et dit « Preparation des arguments "pour" ». Admin : « Actualiser les modeles » renouvelle enfin les preuves, mais SEULEMENT si le releve qui vient d'avoir lieu montre le slug encore present et encore verifie gratuit. Disparu, redevenu payant ou releve en erreur : aucune fraicheur fabriquee. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…pe silencieuse (TASK-1621) Deux corrections mesurees, et l'UX qui les accompagne. 1. La connaissance du modele d'abord, la Boucle ensuite Le module ne lisait plus rien, et le modele repondait « the provided Loop material says nothing about... » : il commentait l'absence d'une matiere qu'on ne lui avait jamais promise. La conversation revient en CONTEXTE — dix derniers messages, bornes au declencheur — jamais en source. ContextBuilder est INCHANGE : les deux bornes voyagent par ContexteIa, le DTO qui porte deja les donnees par operation. Les sept autres capabilities declarant loop.messages rendent la requete d'avant, a la virgule pres. La borne est haute et EXCLUSIVE, pas une exclusion par identifiant : POUR et CONTRE tournent dans deux requetes differees, et une exclusion n'aurait ferme que le premier des deux. Ordre total sur (created_at, id) : created_at est a la seconde. 2. Une reponse coupee ne se publie plus en silence A 900 jetons, 13 des 14 tours « vides » s'arretaient EXACTEMENT au plafond, et 12 reponses publiees comme completes l'atteignaient aussi. Le modele n'etait pas muet : il brulait son budget en raisonnement. - max_tokens 900 -> 2400 ; - finishReason est lu, et ABSENT n'est pas Length : sans mesure, le comportement d'avant tient ; - Length + texte -> PARTIAL, publie et marque, jamais un SUCCESS ; - Length + vide -> OUTPUT_BUDGET_EXHAUSTED, plus EMPTY_MODEL_ANSWER ; - notre propre plafond de caracteres marque la coupe lui aussi. 3. L'ecran Modale supprimee, le bouton EST l'interrupteur. Bulles vert (POUR) et rouge (CONTRE), modele affiche discretement. Rangee des modes visible sur mobile, a defilement horizontal ; contraste du bouton + corrige. Le panneau « Pourquoi cette reponse ? » ne dit plus « MULTI_AI ». Defaut trouve en chemin : aiModeOf() ne connaissait pas multi_ai et retombait sur llm — tout ce qui en dependait restait eteint a l'ecran alors que le code etait la. Tests : 1293 verts sur la salve des voisins, 3 sabotages qui mordent, gate responsive REEL a 390 px. Le seul rouge est pre-existant, mesure sur la branche nue. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
« Windows ou Linux que choisir ? » rendait DEUX reponses en faveur de Linux. Les deux modeles avaient obei : la consigne disait « defends LA proposition exprimee par la question », et une question « A ou B ? » n'en exprime aucune. La clause de repli des personas laissait alors chaque role choisir son sujet, et les deux roles tournent dans deux appels separes : rien ne pouvait les recoordonner. Le referent est fixe dans le socle administrable (v3), au seul rang partage et identique pour les deux appels. Aucun parser PHP : ce n'est pas une analyse syntaxique, c'est une convention de lecture. Et tout se dit en termes POSITIFS de camp DEFENDU. Un premier jet fixait le referent mais laissait au rang du dessous un verbe — « conteste », « 3 a 5 objections » — qui permettait de le contourner : en recette, CONTRE attaquait son propre camp, donc renforcait celui de POUR. Interdire une forme ne ferme pas le comportement. - socle : « TON CAMP EST UNE POSITION QUE TU DEFENDS, jamais une cible que tu attaques », et le piege nomme : « tu ne dois PAS attaquer B, B est TON camp » ; - questions ouvertes : la regle prime sur toute consigne de nombre d'arguments, aucun camp n'est invente, les deux roles repondent la meme chose ; - personas FR + EN reformulees, plus aucune formulation negative. Ni Constitution ni doctrine touchees. Recette reelle, MEME modele pour les deux roles afin d'isoler la semantique : 5 cas sur 5 conformes, 0 echec provider. L'inversion de l'ordre inverse bien les camps. Tests : 63 verts sur le banc moteur, 1298 sur la salve des voisins. Trois sabotages mordent. Le seul rouge est pre-existant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…et les reponses sont deux fois plus courtes (TASK-1621) 1. NOT_APPLICABLE « Quel CMS choisir ? » publiait DEUX bulles disant la meme chose : deux appels payes pour un seul message utile. Le modele annonce desormais le verdict par un marqueur exact, cherche dans le texte BRUT (le sanitiseur pourrait manger les crochets). Aucun parser : une reponse qui PARLE de reformulation sans le marqueur reste une reponse ordinaire, et un test le pin. Quatrieme etat, ni success ni error ni refused — les trois lectures sont fausses. Au ledger, `completed` sans `failure_reason` : l'appel a abouti, le compter comme panne fausserait les sommes de fiabilite. La file s'annule des que le premier role s'abstient. La notice est neutre, sans nom d'assistant et sans « Reessayer » : ce n'est pas un role qui a echoue. 2. Des reponses deux fois plus courtes Mesure : 833-1221 caracteres, 5 puces dans 10 cas sur 12. « 3 a 5 arguments » -> le modele prend toujours le maximum. Et un sous-titre en gras par puce, qui contredisait la regle « une seule phrase en gras » deja presente. Raccourci par la CONSIGNE, jamais par les plafonds : depuis le correctif de troncature, toute coupe par plafond affiche « Reponse ecourtee ». Baisser max_tokens aurait signale comme incompletes des reponses qui ne le sont pas. Resultat mesure en recette reelle : 497-672 caracteres, 3 puces partout, zero gras dans les puces, finishReason Stop partout, aucun badge. 3. Les ecrans Limen retire des deux ecrans de reglage — `catalogueVivant()`, distinct du catalogue complet que `label()` et les bulles anciennes ont encore besoin de lire. On retire un role des ecrans, pas des donnees. Le plugin s'appelle « Pour / Contre » partout ou un humain le lit. La mire d'attente nomme le modele qui prepare. Tests : 90 verts sur les deux bancs, 1394 sur la salve des voisins. Cinq sabotages mordent. Le seul rouge est pre-existant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…progressive (TASK-1621) Point de retour demande par Cyril avant la passe d'affichage progressif : carte 2 colonnes groupee par reply_to_id, mire par role, En attente…, bulle a largeur de colonne. Non recette — la passe suivante corrige le cycle d'affichage, le mobile sans tableau et la barre d'actions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…, deux gestes uniques (TASK-1621) ROOT_CAUSE du constat Cockpit : observation anterieure a la logique $debatEnCours — l'enchainement progressif marchait deja (capture Cyril). Cette passe : carte hidden md:block / bulles md:hidden (pas de tableau sur smartphone, decision Cyril) ; mire et echecs du bandeau en md:hidden (une surface par viewport) ; Repondre retire ; Ajouter au Dossier unique capitalisant le debat entier sous garde anti demi-debat, camp ecourte annonce ; Copier unique repare (x-data remonte a la racine) ; :show-copy-button lie. 36 verts flow + 138 voisins + 4 sabotages rouges. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…ve etendu, recette mesuree (TASK-1621) Le format demande (titre gras + 3 puces) sortait DEJA du modele : le rendu le perdait — whitespace-pre-wrap transformait les sauts de ligne inter-<li> en lignes vides et le preflight retirait list-style. bp-bulle-markdown + regles app.css. Recette Claude for Chrome : T_CARD-T0 = 0 ms (meme mutation), POUR lisible pendant la mire CONTRE, cardGone=0, brouillon de debat verifie, NOT_APPLICABLE sans carte ni bulle. Gate Playwright 5/5 (390/1440). Nettoyage recette : 4 supprimes, 0 restant. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…SK-1621) Le helper posait owner_id ET loop_id ; la contrainte CHECK n'existe que sous PostgreSQL — verts SQLite, 3 rouges au shard 4/6. Un Dossier de Boucle n'a pas de proprietaire personnel : owner_id null. Rejoue en PG (36/36, 144 assertions) et en SQLite (36/36). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…-contre-deux-assistants-sans-rag-et-message-humain-publie-avant-generation TASK-1621 — le plugin devient Pour / Contre : deux assistants sans RAG, message humain publié avant génération
…fie (TASK-1622) model_type + approved_at/approved_by (FK declaree au lifecycle registry) ; assignPaid a 4 marches fail-closed (shortlist config, tarif statique exact non-free via AiPricingCatalog::staticRateFor, slug connu du catalogue) ; garde d'execution branche payante sans aucun repli ; entryFor ignore les lignes payantes (jamais chiffrees 0, meme forgees) ; UI admin par role (type, tarifs par option, badges, audit) ; statut ledger 'completed' -> STATUS_SUCCESS (les lignes du moteur entrent enfin dans les releves — requis avant toute depense payante). 19 verts neufs + 208 voisins + 122 en PG + 4 sabotages rouges (dont 1 test corrige d'un vert pour la mauvaise raison). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
… semantique (TASK-1622) Audit MASTER du changement completed -> success : success est bien le domaine canonique du provider (migration creatrice T1220, 17 writers sur 18) ; completed etait un hapax de ce moteur, 132 lignes d'un seul jour, lu par aucun consommateur. Pas de lecture backward-compatible : admettre les 29 lignes a cout inconnu injecterait 3 operations au quota du mois. Retire le changement COLLATERAL sur ai_interactions (autre table, autre vocabulaire) : un seul des trois chemins de sortie avait ete change. Commentaire reecrit sur la mesure (103 cout connu 0 + 29 cout INCONNU, dont 10 appels payants de T1621 qui n'ont jamais consomme le quota). 4 tests neufs : domaine canonique, non-contamination des traces (les 3 sorties), hors-sujet paye et compte comme tel, et garde TASK-1624 sur CREDITABLE_PROCESSES. 23 verts + 193 voisins + 105 PG + 6 sabotages. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
… ledger (TASK-1622) Pin de semantique demande par MASTER : les TROIS sorties du moteur qui ecrivent une ligne (succes, abstention, reponse vide) sont exercees, et l'ensemble des statuts ecrits doit etre exactement [failed, success]. La colonne est un varchar(10) sans enum : ce test est la seule barriere. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…ssigner le camp (TASK-1622) Decision MASTER du 23/09. La v3 posait l'assignation du camp en TETE : un modele applique alors une etiquette POUR/CONTRE puis cherche ce qu'elle peut vouloir dire. v4 impose un ORDRE en 5 etapes — comprendre, decider s'il y a deux camps, s'abstenir si non, recevoir son camp, VERIFIER chaque argument avant d'ecrire. CHANGEMENT DU SOCLE COMMUN, identique pour tous les modeles : aucune logique par modele ni par provider (doctrine « un contrat commun, plusieurs candidats »). Les bornes de forme sont conservees a l'identique. AiPromptSeeder n'ecrase jamais : v4 est une version NEUVE, v3 desactivee par la regle du plus haut actif. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
Les trois tests qui epinglaient le TEXTE v3 suivent la reecriture : les invariants de camp survivent, seule leur formulation a bouge. Pin NEUF et plus solide sur le point meme de la v4 — l'ORDRE des etapes se mesure par une POSITION (comprendre < s'abstenir < assigner < verifier), pas par une phrase. Sabotage verifie : etape de comprehension deplacee -> rouge. Banc v4 rejoue (66 appels, 0,0145 USD cumule sur les deux bancs, aucun retry) : aucun candidat n'atteint 6/6, la configuration de reference ne change pas. Messages de recette nettoyes (0 restant). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
Socle commun v5 : COMPRENDRE -> CADRER -> ASSIGNER -> VERIFIER -> DEFENDRE. Hypothese MASTER : une sequence POSITIVE conduit mieux qu'un empilement de defenses. Les v3/v4 avaient grossi par sedimentation — chaque defaut de recette ajoutait son garde-fou, si bien que le modele lisait une liste d'interdits avant de savoir ce qu'on attendait de lui. v5 dit la meme chose en cinq gestes affirmatifs. Aucun contenu neuf : une reformulation. Plus la contrainte de langue (meme langue que la question). Socle COMMUN, identique pour tous les modeles. v3 et v4 CONSERVEES en base (desactivees) et dans le seeder : revenir en arriere est un changement de is_active, jamais une reecriture. Pins de tests migres sur les invariants v5 — le camp n'est pas choisi, referent A/B, defendre et pas seulement attaquer — et l'ORDRE des cinq etapes reste mesure par POSITION, pas par une phrase. Baseline avant ce commit : 1bfd609 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…res mesure (TASK-1622) ARBITRAGE MASTER 23/09 05h30. v5 degrade franchement NOT_APPLICABLE : 4/6 modeles s'abstenaient correctement en v3, 3/6 en v4, 1/6 en v5. La sequence positive est plus elegante, mais en faisant de l'abstention un sous-cas du cadrage, elle lui retire le poids qu'elle avait comme regle autonome. v4 retenue : ling sans regression demontree, nova 2/6 -> 5/6, qwen 4/6 -> 5/6, NOT_APPLICABLE nettement meilleur qu'en v5. Le seeder pose is_active = false sur l'entree v5 — sans quoi une base neuve (CI, nouvel environnement) aurait reactive le socle rejete, la regle du seeder etant « la version ACTIVE la plus haute gagne ». Idempotence verifiee par rejeu. Le texte de v5 est conserve avec son motif de rejet : une mesure negative est un resultat. Pins de tests restaures sur les invariants v4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
DEFAUT REEL constate en recette (Cyril, 23/09) : « PC ou Mac que choisir ? » rend NOT_APPLICABLE alors que le membre nomme deux alternatives. Mesure : 3/3 essais, POUR s'abstient ; la file etant annulee des la premiere abstention, CONTRE n'est jamais lance et le membre ne lit que la notice neutre. Trace : 6 abstentions recentes, toutes du role aperio. CAUSE mesuree : v3 traitait la question 3/3. Ce n'est pas le modele qui a change, c'est le socle. Piste de la virgule ECARTEE par la mesure (« PC ou Mac, que choisir ? » echoue aussi). Reste : le modele juge que PC et Mac ne sont pas nettement opposables, et l'etape de cadrage de v4 lui donne la permission de s'arreter la. CORRECTION : la reconnaissance d'une comparaison A/B explicite PRECEDE desormais l'abstention, qui devient le DERNIER recours ; le socle dit de respecter le cadre pose par le membre meme si les alternatives se chevauchent techniquement ou sont nommees familierement. Aucun cas special PC/Mac : la regle ne nomme aucun produit. v4 conservee. Pins migres sur les invariants v6, dont l'ORDRE des etapes mesure par position : comparaison A/B AVANT abstention. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…ABLE (TASK-1622) Checkpoint avant la passe « aider a reformuler ». Baseline de cette passe : socle v6 (A/B avant abstention), HEAD v6 = 35db2a6. TASK repasse IN_PROGRESS / LOCKED — le TASK file (gitignore) porte le detail. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…TASK-1622) Quand « Pour / Contre » conclut qu'une question n'ouvre aucun debat, le MEME appel provider propose desormais une reformulation exploitable. Le membre clique, le texte arrive dans le composeur, il le modifie s'il veut et l'envoie LUI-MEME : l'IA propose, l'humain garde le dernier geste. Audit avant d'inventer. Le jeton [[SUGGESTION]] etend le seul idiome de contrat de prompt du depot et reste RETRO-COMPATIBLE (str_contains sur le marqueur d'abstention inchange : les socles v3-v6 s'abstiennent exactement comme avant). Canal DEDIE sur AssistantOutcome plutot que followUps : un approfondissement prolonge une reponse, une reformulation la remplace. useSuggestion() ne prend AUCUN parametre — le serveur relit sa propre suggestion, rien ne voyage depuis le client. Piege evite grace a l'audit : le composeur est en wire:model differe et son hasText pilote le bouton d'envoi ; sans l'evenement composer-filled, le membre aurait lu sa question sans pouvoir l'envoyer. Socle v7 derive de v6 (v6 conservee). Il interdit d'inventer un produit que le membre n'a pas nomme : pas de suggestion fidele possible = pas de suggestion, et l'ecran demande une precision. 41 verts (+5), 169 avec les voisins, 65 en PG, 3 sabotages rouges. Micro-recette reelle Ling : 5 cas sur 6 conformes ; le cas « Quelle est la difference entre X et Y ? » declenche encore un debat — rapporte a MASTER, non corrige seul (le corriger risque de ramener le defaut « PC ou Mac » que cette passe vient de fermer). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…622) « Quelle est la difference entre PC et Mac ? » declenchait un debat. L'etape 2 de v6/v7 ne posait qu'une question — deux alternatives sont-elles nommees ? — et deux noms suffisaient a fabriquer deux camps. Ce n'etait pas un accident : cette detection agressive est ce qui avait repare « PC ou Mac que choisir ? ». Le defaut etait le prix de la reparation. v8 (derivee de v7, v7 conservee) pose DEUX conditions conjointes a l'etape 2 : deux alternatives nommees ET une intention de les departager. Les formes informatives sont nommees explicitement, sans quoi la regle serait restee abstraite. Le cas ecarte ne tombe pas dans le vide : l'etape 4 reformule une question de COMPARAISON en question de CHOIX entre les deux memes noms, dans l'ordre ou le membre les a donnes — rien n'est invente. Garde soeur indispensable : une question qui cherche a FAIRE COEXISTER les deux ne doit pas se voir proposer de les departager, le membre possede deja les deux ; sans elle, la reformulation aurait casse « Comment faire communiquer un PC et un Mac ? ». Aucun cas special PC/Mac : la regle ne nomme aucun produit, et un test l'epingle. Recette reelle Ling, 6 cas sur 6 conformes, « PC ou Mac » intact dans les deux ordres. 171 verts, 136 en PG, 2 sabotages rouges. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…ee-verifies-payants-approuves TASK-1622 — Pour / Contre : modèles FREE vérifiés / PAYANTS approuvés
…1623)
Test ROUGE avant correction. Les quatre chemins d'upload image passent
l'objet fichier a Intervention Image, qui le resout par getPathname().
TemporaryUploadedFile::getPathname() rend Storage::disk($tmp)->path($p), et
FilesystemAdapter::path() ne fait que prefixer avec la racine du disque :
absolue en local, VIDE sur s3/gcs. Le chemin rendu est alors relatif —
« livewire-tmp/xxx.png » — et Intervention leve DirectoryNotFoundException.
Le point qui a demande le plus de soin : aucun test existant ne POUVAIT
attraper ce bug. Livewire::test()->set('image', ...) passe par
FileUploadConfiguration::disk(), qui rend « tmp-for-tests » en dur des que
l'app tourne en test, et storage() en fait un Storage::fake() local. Le
harnais de test de Livewire ne peut pas produire la condition de production ;
un test ecrit par ce chemin serait reste vert quoi qu'il arrive.
Le banc construit donc un vrai TemporaryUploadedFile sur un disque dont le
FilesystemAdapter recoit root vide : path() rend un chemin relatif, comme s3,
sans reseau — tandis que le contenu est reellement servi. C'est exactement ce
que fait un stockage distant.
5 rouges, avec l'exception et le message exacts du rapport PROD. Les 2 verts
comptent autant : la premisse et le cas local passent deja, donc le banc ne
rougit pas par construction.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…hemin (TASK-1623)
Quatre corrections ciblees, une ligne chacune : Image::decode($file) devient
Image::decode($file->get()). C'est l'idiome deja en place dans le depot
(GenerateServiceThumbnail lit son image par $disk->get($path)), et il ne
demande ni service generique, ni abstraction, ni refactor.
Sur un stockage temporaire distant, TemporaryUploadedFile::getPathname()
rend « livewire-tmp/xxx.png » — un chemin qui n'existe localement nulle part,
parce que FilesystemAdapter::path() ne fait que prefixer avec la racine du
disque, et que cette racine est vide sur s3/gcs. Le contenu, lui, a toujours
ete lisible : c'est lui qu'il fallait passer.
Le banc de reproduction passe de 5 rouges a 7 verts, et le cas local est
verifie avec le meme objet de production sur un disque a racine absolue.
Sabotage : redonner l'objet fichier a LoopChat fait rougir la garde de
non-retour, qui balaie les quatre surfaces d'un coup.
Hors scope, verifie et non modifie : ServiceController et ProfileController
recoivent un UploadedFile d'une requete HTTP classique, dans le tmp local de
PHP — ils ne presentent pas ce defaut.
118 verts en PostgreSQL. Le seul rouge voisin, « workspace cards shell », est
porte par #[Group('ci-known-red')] et documente comme deja rouge sur develop
avant TASK-1112 : il est hors gate GitHub, et hors de cette TASK.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…vewire-stockage-distant TASK-1623 — Hotfix upload image Livewire stockage distant
TASK OPS d'audit et de synchronisation PROD. Aucune migration, aucun code applicatif : le contrat TASK/VERSION du depot s'applique quand meme, et c'est l'arbitrage MASTER. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3
…ation-prod-laravel-cloud TASK-1624 — Preparation synchronisation PROD Laravel Cloud
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release PROD 1.613 → 1.624
Synchronisation de
mainsurdevelopaprès 11 merges de PR accumulés depuis le 20/09. Motif prioritaire : TASK-1623 corrige un bug d'upload d'image incompatible avec le stockage temporaire distant de PROD.Le correctif qui motive cette release
En PROD,
filesystems.default = public,public.driver = s3, et Livewire n'a pas de disque temporaire dédié — il retombe donc sur S3.TemporaryUploadedFile::getPathname()y rend un chemin relatif (livewire-tmp/xxx.png), qu'Intervention Image ne peut pas résoudre :DirectoryNotFoundException.Quatre corrections d'une ligne,
Image::decode($file)→Image::decode($file->get())— le contenu, jamais le chemin — sur ChatLoop, MessageThread, CreateFeedPost et EditFeedPost. Cette configuration a été mesurée sur PROD, pas supposée.TASKS_INCLUDED
T1614, T1615, T1616, T1617, T1618, T1619, T1620, T1621, T1622, T1623, T1624.
Migrations : 5, toutes SAFE
create_organization_loop_plugins_tablecreate_loop_plugins_tablecreate_loop_ai_assistants_tablecreate_loop_plugin_ai_models_tableadd_paid_approval_to_loop_plugin_ai_models_tableListe établie par différence —
migrate:statussur PROD ne voit que les fichiers du code déployé. 281 migrations dansdevelopmoins 276 réellement jouées en PROD = 5. Contrôle inverse fait, et vérification table par table sur la base : les 4 tables n'existent pas. Les deux méthodes concordent.Aucune colonne
NOT NULLsur une table peuplée, aucun index sur une table volumineuse, aucune suppression. Volumes réels : 2 organisations, 47 utilisateurs, 4 Boucles. Downtime attendu nul.Un rollback code après ces migrations est sûr : elles n'ajoutent que des structures que le code 1.613 ignore totalement.
Ce qui reste éteint
Tout le bloc T1614→T1622 est fermé par défaut : la chaîne catalogue → disponibilité Organization → activation Boucle lit « absence de ligne = non disponible ». Les tables arrivant vides, le plugin Pour/Contre est invisible et inerte. Aucune dépense IA possible : sans ligne dans
loop_plugin_ai_models, un assistant est refusé avant tout appel provider.7 variables d'environnement nouvelles, toutes avec un défaut — aucune bloquante.
Aucun changement de dépendance :
composer.json,composer.lock,package.jsonne bougent pas.Protection de la base
PITR Neon 7 jours confirmé par l'API Laravel Cloud (
db-cluster:get,config.retention_days = 7), recoupé pardb-cluster:listet corroboré pardb-restore:create --point-in-time.🤖 Generated with Claude Code
https://claude.ai/code/session_01JqwjarKwhSbpzdPxLAmNh3