init plans #177

Merged
Nora merged 42 commits from feature/avis-livre-or into develop 2026-09-06 20:03:49 +00:00
Owner
No description provided.
init plans
Some checks failed
CI / Renderer - couverture JS (pull_request) Has been skipped
CI / Renderer - audit des dépendances JS (pull_request) Successful in 44s
CI / Renderer - tests + lint JS (pull_request) Has been cancelled
CI / PHP - tests + static + lint (pull_request) Has been cancelled
a78fc4e057
init docs
All checks were successful
CI / Renderer - couverture JS (pull_request) Has been skipped
CI / Renderer - audit des dépendances JS (pull_request) Successful in 1m4s
CI / Renderer - tests + lint JS (pull_request) Successful in 5m58s
CI / PHP - tests + static + lint (pull_request) Successful in 7m59s
2725586619
Un corps de message est nettoyé avant d'être mesuré (mille espaces n'est
pas un message), et refuse d'exister au-delà du plafond de sa nature :
1 000 signes en livre d'or, 2 000 en signalement. Comptage en signes
(mb_strlen), pas en octets.

Ajoute la règle de périmètre mago du nouveau module : App\Feedback\Domain
ne voit que le natif et le noyau Shared.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Quatre formes refusées côté guestbook, insensibles à la casse : http://,
https://, www. et un mot.tld nu collé au point (une phrase ponctuée
« Bravo. Superbe travail. » passe). Le refus est loud : constructeur nommé
linkNotAllowed() sur la même exception, pour que le contrôleur le montre
à l'humain plutôt que de l'avaler.

L'asymétrie est le comportement : un signalement n'est jamais publié,
il n'appâte donc aucun spammeur SEO. C'est ce qui autorise l'indexation
du mur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Le nom affiché sous une signature est trimé, non vide, plafonné à 60
signes, et ne peut pas servir de support de lien : un pseudo « spam.ru »
sur un mur indexé, c'est exactement le spam que le refus de liens écarte
côté corps.

Le motif de lien est recopié plutôt que partagé : deux VO indépendants
valent mieux qu'un couplage prématuré. L'optionalité du nom (obligatoire
en livre d'or, facultatif en signalement) vivra dans l'entité, pas ici —
un AuthorName existe ou n'existe pas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
La provenance d'un message est choisie par la machine dans une liste
blanche ; toute valeur inconnue arrivant de la requête retombe sur
« site », sans exception. Le ?expo= est falsifiable et sans importance :
il ne prépare qu'un badge sur une entrée qui passe de toute façon sous
l'œil d'un humain.

Une expo de plus = une ligne dans ALLOWED, jamais un champ visiteur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
feat(feedback): VO ContextPath, un chemin interne ou rien (T05)
All checks were successful
CI / Renderer - couverture JS (pull_request) Has been skipped
CI / Renderer - audit des dépendances JS (pull_request) Successful in 59s
CI / Renderer - tests + lint JS (pull_request) Successful in 6m51s
CI / PHP - tests + static + lint (pull_request) Successful in 7m46s
94c7f6b852
Le ?from= du lien de pied de page devient un chemin interne exploitable
pour le triage, ou null — jamais une porte de redirection ouverte :
doit commencer par /, refuse le protocol-relative (// et /\), refuse
tout schéma, plafonne à 200 signes. Une valeur douteuse vaut null, pas
une erreur : le visiteur n'a rien fait de mal, il a juste un lien tordu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Deux natures, quatre états. Le plafond de corps et le refus des liens
sont portés par la nature, dans un match fermé : l'appelant demande, il
ne décide pas. La nature est figée à la création, aucune méthode ne la
change.

MessageStatus n'a aucune méthode : les transitions vivent dans l'entité.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Un message naît par l'une des deux fabriques nommées, avec les seuls
champs que sa nature autorise : le nom est obligatoire en livre d'or,
le user-agent et le chemin de contexte n'existent que pour un
signalement. Constructeur privé, aucun setter public.

L'identifiant vient de l'appelant et l'horloge est injectée : le Domain
ne génère ni UUID ni date. Rien d'identifiant n'est stocké — ni IP, ni
cookie, ni session.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
publish() refuse un report, markHandled() refuse une signature, et toute
transition depuis un état terminal lève. La garde vit dans l'entité, pas
dans le contrôleur : aucune manipulation du back-office ne peut publier
un signalement.

Une seule bascule privée porte la garde d'état, les trois portes
publiques n'ajoutent que leur garde de nature. La date de modération est
un paramètre, jamais un « maintenant » interne.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Le module existe enfin comme module : PSR-4, bloc de mapping XML, alias de
ports, périmètre mago. La table visitor_message n'a AUCUNE clé étrangère
— un retour doit survivre à toute purge, même raisonnement que le journal
de curation — et ne stocke rien d'identifiant : ni IP, ni cookie, ni
session. Son index (status, created_at) est la requête du mur.

Les quatre VO se mappent par des types DBAL, comme seed et palette côté
Studio : le Domain reste sans attribut Doctrine. La relecture passe par
un fromStorage() qui ne revalide pas — une ligne écrite hier reste
lisible si les règles de forme bougent demain.

Migration jouée sur dev et sur test ; doctrine:schema:validate ne signale
aucune dérive sur cette table.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Une requête DBAL nue, sans cache : la table est petite, et brancher la
projection en cache de la galerie imposerait un VERSION à bumper pour un
mur de mots. La double garde kind + status est volontaire — l'entité
interdit déjà de publier un signalement, la requête tient la porte une
seconde fois, y compris contre une ligne forcée en base.

Aucune locale ne filtre la lecture : on ne traduit jamais la parole de
quelqu'un, la locale voyage avec l'entrée pour poser son lang. La page a
la forme de l'ArchivePage du musée, celle que la barre de pagination sait
déjà rendre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Un retour visiteur n'est pas de l'argent et sa ligne est déjà commitée
quand l'événement part : perdre la notification ne perd aucune donnée.
Il va donc sur async (Redis, jetable/rejouable), jamais sur critical, que
le dépôt réserve aux promesses de livraison.

L'événement reste dans App\Feedback\Application : aucun autre module ne
l'écoute, il n'a rien à faire dans App\Shared\Event.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Dans cet ordre : la ligne est commitée avant que l'événement parte, donc
perdre la notification ne perd aucune donnée.

Écart assumé avec la fiche : le cas d'usage ne prend ni générateur d'id
ni horloge. L'entité arrive déjà assemblée par le contrôleur — c'est lui
qui tient la requête, ses VO, l'identifiant et la date — et deux
dépendances jamais lues seraient du poids mort que l'analyse signalerait.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
feat(feedback): SpamGuard, le honeypot rempli fait « comme si » (T13)
All checks were successful
CI / Renderer - couverture JS (pull_request) Has been skipped
CI / Renderer - audit des dépendances JS (pull_request) Successful in 1m3s
CI / Renderer - tests + lint JS (pull_request) Successful in 6m36s
CI / PHP - tests + static + lint (pull_request) Successful in 7m31s
5842434164
Un champ appât invisible rempli classe la soumission comme robot :
l'appelant abandonnera en silence et répondra une 302 normale, le robot
croit avoir réussi. Jugé en premier, c'est le moins cher.

Ni captcha ni service tiers : pas de JS externe, pas de transfert de
données hors UE. Écart avec la fiche : le Domain du dépôt est sans
horloge PSR (la garde de périmètre mago l'interdit), l'heure arrive donc
en paramètre, comme les dates des transitions de l'entité.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Moins de trois secondes ou plus de deux heures après l'affichage du
formulaire : robot. Valeur absente, non numérique ou dans le futur aussi
— on ne devine pas. Les deux bornes exactes sont figées par leurs propres
tests, ce sont elles le contrat.

Même traitement que le honeypot : drop silencieux, 302 normale. Ces deux
gardes arrêtent les robots génériques, pas un spammeur ciblé ; le recours
prévu pour celui-là est la fermeture temporaire du livre d'or.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
POST classique, aucune ligne de JS : Turbo Drive est désactivé sur ce
site. La page se déclare noindex, nofollow — un formulaire privé n'a rien
à faire au crawl, et sans ça autant de variantes ?from= que de pages y
entreraient.

Le champ appât est sorti du flux visuel et du parcours clavier
(visually-hidden, aria-hidden, tabindex -1) ; le champ caché rendered_at
porte l'horodatage que le piège temporel relira. Le ?from= est déjà lu et
filtré par ContextPath ici — sa fiche T16 n'ajoutera que ses tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
test(feedback): le ?from= pré-rempli, la valeur hostile disparaît (T16)
All checks were successful
CI / Renderer - couverture JS (pull_request) Has been skipped
CI / Renderer - audit des dépendances JS (pull_request) Successful in 57s
CI / Renderer - tests + lint JS (pull_request) Successful in 6m21s
CI / PHP - tests + static + lint (pull_request) Successful in 7m0s
584962886b
Deux tests fonctionnels ferment la garde : un chemin interne repart en
champ caché, une URL absolue laisse le champ vide, sans un mot à
l'utilisateur. Le code lu côté contrôleur était déjà posé au commit
précédent — ici, ce sont les tests qui manquaient.

La page reste canonique sur l'URL propre : le bloc canonical de base
rend le chemin sans query string, il n'y a rien à surcharger.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Nora added 11 commits 2026-09-06 17:35:20 +00:00
Le POST suit l'ordre de la fiche : garde anti-robot d'abord (verdict Bot =
même 302, rien en base, un info au log), puis le formulaire, puis
l'enregistrement en pending suivi d'une redirection vers l'URL propre.

Les VO revalident ce que le formulaire laisse passer - un nom d'auteur
porteur de nom de domaine, par exemple : leur refus revient en erreur de
champ traduite, plus en 500.

base.html.twig ne rend aucun flash : le gabarit du formulaire boucle
lui-même sur app.flashes, sans changer l'aspect des autres pages.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Le budget compte les soumissions, pas les lectures : consommé sous
`isSubmitted()`, relire le formulaire six fois de suite reste gratuit,
et le flot ne coûte qu'un compteur.

Le pool `cache.rate_limiter` n'est pas basculé en `array` sous `when@test` :
les deux tests fonctionnels du module le vident, sinon le budget d'une IP
fuit d'un test à l'autre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Publier un prénom est une publication de donnée personnelle : c'est le seul
endroit du site qui ait besoin d'un consentement, il est ici, obligatoire
(Assert\IsTrue côté serveur, pas seulement l'attribut HTML).

Contrairement au signalement, la page est indexée : elle ne surcharge pas le
bloc robots. Le nom d'auteur est obligatoire - une signature anonyme n'est
pas une signature.

Le POST ne persiste encore rien : c'est T20.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Le ?expo= est lu une seule fois, à l'ouverture, et reposé en champ caché ; le
POST part sur l'URL propre. Une valeur hors liste blanche retombe sur « site »
sans un mot : ce n'est pas la faute du visiteur, et la modération reste la garde.

Second cas d'usage réel : les trois gardes communes aux deux formulaires
(budget par adresse, verdict anti-robot, refus de VO en erreur de champ)
partent dans SubmissionGuards. Les deux contrôleurs repassent sous le budget
de paramètres, les deux pragmas mago tombent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Le port de lecture rend la page, le gabarit boucle. Chaque entrée porte son
lang="xx" : on ne traduit jamais la parole de quelqu'un. Le badge d'expo se
pose quand la provenance commence par « expo: », son libellé est une clé par
expo - une expo de plus = une ligne dans la liste blanche + une clé.

Aucun |raw : le corps est du texte. Aucune entrée en attente sur le mur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
La convention du musée, recopiée : lecture tolérante du ?page= (getInt lèverait
un 400 sur « abc »), 404 au-delà de la dernière page, page 1 sur l'URL propre,
barre en liens <a> sans frame Turbo.

Le canonical porte le numéro dès la page 2 et jamais le ?expo= : sans ça, le
bouton d'expo fabriquerait un doublon indexable de la page la plus visible.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Le panneau A4 est imprimé et sur son mur pour quatre mois : ajouter un QR code
imposerait de réimprimer et de retourner sur place, un bouton est un
déploiement. Locale fixée à fr comme les libellés voisins, rel=nofollow parce
que la cible porte un paramètre de provenance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Le lien transmet pathInfo, pas requestUri : le VO ContextPath refuserait de
toute façon ce qui n'est pas un chemin interne. Symfony n'encode pas les
barres obliques d'une valeur de requête, le chemin voyage tel quel.

Clé FR posée ; le tour des six catalogues est T34.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Fonction pure de rendu, comme ses voisines : le service Node pose un lien vers
le formulaire Symfony, il n'écrit rien. Onglet neuf pour ne pas perdre la
position dans la salle, provenance galerie-3d déclarée dans l'URL.

Le lien sort du role="group" des bascules : ce n'est pas un réglage d'affichage.

⚠️ Le client est cuit au build : rebuild de l'image gallery3d + --force-recreate
au go-live, sinon le lien n'existe pas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Jamais d'écriture Doctrine directe sur l'entité riche depuis le back-office.
La garde « un signalement n'est jamais publié » reste dans l'entité : le cas
d'usage la laisse remonter au lieu de la dupliquer.

MessageNotFound pour un identifiant qui ne résout rien : le back-office le
traduira en message à l'exploitant, pas en 500 muet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
feat(feedback): RejectVisitorMessage et MarkReportHandled (T27)
Some checks failed
CI / Renderer - couverture JS (pull_request) Has been skipped
CI / Renderer - audit des dépendances JS (pull_request) Successful in 55s
CI / Renderer - tests + lint JS (pull_request) Successful in 6m28s
CI / PHP - tests + static + lint (pull_request) Failing after 7m23s
1b107130be
Trois classes nommées plutôt qu'une classe paramétrée par l'action : ça se lit
mieux et ça coûte moins qu'une indirection. « Traité » reste le vocabulaire du
signalement, la garde vit dans l'entité.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Lecture seule : l'entité est riche et sans setter, les formulaires NEW/EDIT
n'auraient rien où écrire. Les VO sont ancrés sur leur scalaire public
(body.value, source.value) - un VO posé nu sur un TextField fait un 500, et un
test couvre le détail d'un message aux VO facultatifs nuls.

Le test vit dans tests/Functional/Admin comme les autres CRUD du dépôt, pas
dans tests/Integration comme l'annonçait la fiche.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Publier n'apparaît que sur une signature en attente, Marquer traité que sur un
signalement : le displayIf évite un geste voué à l'échec, la vraie garde reste
dans l'entité. Une transition refusée revient en flash danger, jamais en 500.

Piège : $this->useCase(...) sur une propriété invocable est lu comme un appel de
méthode - il faut $this->useCase->__invoke(...).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Lecture DBAL nue injectée dans le tableau de bord : le menu observe, il ne
touche jamais au dépôt Doctrine de l'entité riche. Le badge rend le coût de la
modération visible, il ne l'automatise pas.

Relevé au passage : EasyAdmin 5 rend la barre latérale dans
<nav class="ea-sidebar-content">, pas .main-menu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
feat(admin): la tuile « Retours à traiter » au tableau de bord (T31)
Some checks failed
CI / Renderer - couverture JS (pull_request) Has been skipped
CI / Renderer - audit des dépendances JS (pull_request) Successful in 54s
CI / Renderer - tests + lint JS (pull_request) Successful in 6m4s
CI / PHP - tests + static + lint (pull_request) Failing after 6m48s
5994c949a4
Le provider réutilise la lecture DBAL du badge de menu plutôt que d'écrire une
seconde requête : un seul endroit sait ce qu'est « un retour en attente ».
Compte brut, jamais un taux.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Nature, provenance, extrait et lien droit sur la fiche de modération. En
français seulement : le back-office n'est pas localisé, donc pas de
LocalizedMailer ni de domaine de traduction.

ADMIN_EMAILS gagne un second binding - aucun nouveau secret. Une ligne
supprimée entre l'envoi et la consommation sort par un log, jamais par un
retry sans fin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Le lastmod vient de la dernière signature publiée, pas d'une date figée. La
boucle existante génère déjà une <url> par locale avec ses alternates : une
ligne suffit. Mur vide = lastPublishedAt() nul, et le gabarit omet déjà
<lastmod> dans ce cas.

Aucun JSON-LD Review ni Comment : ce ne sont pas des avis produit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
28 clés × 6 locales, traduites pour de vrai - pas de français recopié en
allemand. Le test interroge les catalogues, pas trans() : avec fallbacks: [fr],
une clé absente rendrait le français sans broncher et le RED n'existerait pas.

TranslatorBagInterface vit dans Symfony\Component\Translation, pas dans
Symfony\Contracts\Translation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
docs(legal): dire ce qu'on collecte, et combien de temps on le garde (T35)
Some checks failed
CI / Renderer - couverture JS (pull_request) Has been skipped
CI / Renderer - audit des dépendances JS (pull_request) Successful in 46s
CI / Renderer - tests + lint JS (pull_request) Successful in 5m57s
CI / PHP - tests + static + lint (pull_request) Failing after 6m46s
a4fa40e04f
Les six pages de confidentialité décrivent la collecte des retours : message,
langue, prénom, e-mail facultatif, et le user-agent pour un signalement - ni IP,
ni cookie, ni identifiant de session. Signalement supprimé 12 mois après
traitement, signature en ligne jusqu'à demande de retrait.

Les mentions légales déclarent enfin un canal de contact : le site n'en avait
aucun.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Audit /a11y-twig des quatre gabarits : deux manquements majeurs. Un résumé
role="alert" en tête de formulaire, chaque ligne ancrée sur le champ fautif
(WCAG 3.3.1) - au clavier, on y arrive en une touche. Et autocomplete name /
email sur les champs qui le méritent (WCAG 1.3.5).

Le budget de méthodes mago a mordu sur ReportSubmissionTest : les deux tests
d'une même soumission vide sont fusionnés, pas suppressés par un ignore.

Reste hors périmètre, à porter au backlog : le site entier n'a ni lien
d'évitement, ni id="main-content", ni déclaration d'accessibilité.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
L'item produit disparaît : le code est livré. Reste une séquence de go-live en
quatre gestes, dont le rebuild de l'image gallery3d (le lien d'overlay est cuit
au build) et le choix du plafond du limiteur.

Nouvel item : les trois manquements d'accessibilité relevés par l'audit de la
tranche, transverses donc hors de son périmètre - lien d'évitement,
id="main-content", et la déclaration d'accessibilité qu'impose l'EAA. Le
formulaire de signalement est déjà le mécanisme de retour qu'elle doit citer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
fix(guard): remettre le module Feedback dans son périmètre architectural
All checks were successful
CI / Renderer - couverture JS (pull_request) Has been skipped
CI / Renderer - audit des dépendances JS (pull_request) Successful in 1m3s
CI / Renderer - tests + lint JS (pull_request) Successful in 6m55s
CI / PHP - tests + static + lint (pull_request) Successful in 7m42s
e7d1dc97b1
`mago guard` n'était pas dans ma boucle (CLAUDE.md annonçait « guard » sur la
ligne lint/analyze/fmt, qui ne le lance pas) : 59 brèches accumulées.

Quatre corrections, dont trois sont du code, pas de la configuration :
- le handler de notification ne connaît plus EasyAdmin : le lien profond passe
  par un seam `VisitorMessageAdminLink`, confiné au sous-dossier Admin ;
- le sitemap (Gallery) ne référence plus le Domain de Feedback mais le seam
  `App\Shared\Feedback\GuestbookLastPublication` - un module ne lit jamais le
  Domain d'un autre ;
- `SubmissionGuards` n'est pas un contrôleur : il quitte le dossier Controller,
  dont la règle structurelle impose le suffixe.

Seule concession à la config : le sous-dossier `Feedback\Infrastructure\Admin`
gagne le layer easyadmin, règle scopée calquée sur Studio et Shop - le reste du
module ne le voit pas.

CLAUDE.md corrigé : la ligne de commandes lance maintenant `mago guard`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
feat(design): les deux pages de retours parlent la langue du site
Some checks failed
CI / Renderer - couverture JS (pull_request) Has been skipped
CI / Renderer - audit des dépendances JS (pull_request) Successful in 53s
CI / Renderer - tests + lint JS (pull_request) Successful in 6m8s
CI / PHP - tests + static + lint (pull_request) Has been cancelled
1361e468e2
Le livre d'or et le signalement sortaient en Bootstrap brut - container
imbriqué, alert bleue, bouton bleu, formulaire au thème par défaut nu -
sur un site qui est sombre, serif, mono et vert acide partout ailleurs.

Elles reprennent maintenant l'en-tête commune (surtitre mono, titre en
display serif, chapô en largeur de lecture), la carte de formulaire de
l'éditeur de citation (label « // », champ à filet acide, bouton plein
qui se vide au survol), et la barre de pagination du musée - les mêmes
classes, pas une seconde feuille de style qui divergerait.

Le markup rendu ne bouge pas : le thème de formulaire reste celui par
défaut, la classe voyage par `row_attr`, et l'ordre label/case du
consentement est rétabli en CSS. Un surtitre = deux clés, donc six
langues et deux lignes de plus au test de catalogue.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
fix(galerie3d): le signalement rentre dans la barre de réglages
All checks were successful
CI / Renderer - couverture JS (pull_request) Has been skipped
CI / Renderer - audit des dépendances JS (pull_request) Successful in 46s
CI / Renderer - tests + lint JS (pull_request) Successful in 6m10s
CI / PHP - tests + static + lint (pull_request) Successful in 7m8s
74da4f89b5
Le lien était posé DEHORS de la boîte, en frère de `.reglages`, et sans
aucune règle CSS : marge zéro, donc à ras du bord de l'écran, désaligné
d'avec les deux bascules juste au-dessus, et rendu par le navigateur en
bleu souligné - la seule tache bleue d'une salle noire et vert acide.

Il devient le troisième étage de la même boîte, sous un filet : mono,
capitales, `--faint`, flèche en accent-dim comme les autres liens
sortants de la salle (`.salles__hub`, `.cartel__manifeste`), 44 px de
haut comme les bascules. Les deux boutons partagent la largeur du bloc.

`role="group" aria-label="Affichage"` se resserre sur les deux bascules :
il décrivait aussi un lien de signalement, qui n'est pas un affichage.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015j87XomTCvE9YEUgHpSJs3
Nora merged commit 740609f34a into develop 2026-09-06 20:03:49 +00:00
Nora deleted branch feature/avis-livre-or 2026-09-06 20:03:50 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Nora/ArtAutomatic!177
No description provided.