# Lot 1 — Fondations (scaffold + config + auth + base UI Neon Nocturne) — Plan d'implémentation

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Mettre en place un projet Symfony 7 fonctionnel, connecté à MySQL, avec comptes clients (inscription/connexion) et une page d'accueil au design « Neon Nocturne ».

**Architecture:** Application monolithique Symfony (rendu Twig). AssetMapper pour le CSS (aucun build Node). Sécurité Symfony pour l'authentification par formulaire. Doctrine ORM sur MySQL (fourni par WAMP).

**Tech Stack:** Symfony 7.x, PHP 8.3, Doctrine ORM, MySQL (WAMP), Twig, AssetMapper, PHPUnit (WebTestCase).

---

## Contexte d'environnement (vérifié le 2026-05-21)

- Le dépôt contient déjà : `.git/`, `docs/`, `CLAUDE.md`. **Ne pas les écraser.**
- `composer create-project` exige un dossier vide → on scaffolde dans un dossier temporaire puis on fusionne à la racine.
- WAMP : MySQL sur `127.0.0.1:3306`, utilisateur `root` **sans mot de passe** par défaut. Adapter si la config locale diffère.
- La spec de référence : `docs/superpowers/specs/2026-05-21-cartecadeau-design.md`.

## Carte des fichiers (créés/modifiés dans ce lot)

- `composer.json`, `symfony.lock`, `bin/console`, `public/index.php`, `config/**`, `.env` — générés par le scaffold.
- `.env.local` (créé, **git-ignoré**) — `DATABASE_URL` + clés Wupex/CinetPay vides.
- `src/Entity/User.php` — entité client (UserInterface).
- `src/Repository/UserRepository.php` — repository (généré par maker).
- `src/Controller/HomeController.php` — page d'accueil.
- `src/Controller/RegistrationController.php` + `src/Form/RegistrationFormType.php` — inscription.
- `src/Controller/SecurityController.php` — connexion/déconnexion.
- `templates/base.html.twig` — layout Neon Nocturne.
- `templates/home/index.html.twig`, `templates/registration/register.html.twig`, `templates/security/login.html.twig`.
- `assets/styles/app.css` — design tokens + composants Neon Nocturne.
- `config/packages/security.yaml` — firewall, hasher, access control.
- `tests/Controller/HomeControllerTest.php`, `tests/Controller/RegistrationControllerTest.php`, `tests/Controller/SecurityControllerTest.php`.
- `migrations/VersionXXXX.php` — table `user`.

---

## Task 1 : Scaffolder le projet Symfony à la racine

**Files:**
- Create: tout l'arbre Symfony à la racine (via fusion depuis un dossier temporaire).

- [ ] **Step 1 : Créer le projet Symfony dans un dossier temporaire**

Run :
```bash
cd "c:/wamp64/www/CARTECADEAU"
composer create-project symfony/skeleton:"7.*" .symfony-tmp --no-interaction
```
Expected : installation OK, dossier `.symfony-tmp/` créé avec `bin/`, `config/`, `public/`, `src/`, `.env`.

- [ ] **Step 2 : Fusionner le scaffold à la racine sans écraser docs/CLAUDE.md/.git**

Run :
```bash
cd "c:/wamp64/www/CARTECADEAU"
cp -r .symfony-tmp/. .
rm -rf .symfony-tmp
ls bin config public src .env
```
Expected : `bin config public src .env` listés à la racine ; `docs/` et `CLAUDE.md` toujours présents.

- [ ] **Step 3 : Ajouter le webapp pack + maker**

Run :
```bash
composer require webapp --no-interaction
composer require --dev symfony/maker-bundle --no-interaction
```
Expected : installation de twig, form, validator, security, doctrine, mailer, messenger, asset-mapper, maker.

- [ ] **Step 4 : Vérifier que l'app démarre**

Run :
```bash
php bin/console about
```
Expected : tableau « Symfony » avec version 7.x, environnement `dev`. Aucune erreur fatale.

- [ ] **Step 5 : Commit**

```bash
git add -A
git commit -m "feat: scaffold Symfony 7 webapp à la racine du projet"
```

---

## Task 2 : Connecter MySQL (WAMP) et créer la base

**Files:**
- Create/Modify: `.env.local`
- Modify: `.env.test`

- [ ] **Step 1 : Détecter la version du serveur MySQL**

Run :
```bash
php -r "$p=new PDO('mysql:host=127.0.0.1;port=3306','root','');echo $p->getAttribute(PDO::ATTR_SERVER_VERSION);"
```
Expected : une version s'affiche (ex. `8.0.31` ou `5.7.x` ou `10.x-MariaDB`). Noter cette valeur pour `serverVersion`.

- [ ] **Step 2 : Écrire `.env.local` avec la connexion + clés vides**

Créer `.env.local` (remplacer `<VERSION>` par la valeur du Step 1 ; si MariaDB, préfixer par `mariadb-`) :
```dotenv
APP_ENV=dev
DATABASE_URL="mysql://root:@127.0.0.1:3306/cartecadeau?serverVersion=<VERSION>&charset=utf8mb4"

# Wupex (à renseigner)
WUPEX_API_URL=https://sandbox-service.wupex.com
WUPEX_API_KEY=
WUPEX_MERCHANT_CODE=
WUPEX_LANGUAGE=fr-FR

# CinetPay (à renseigner)
CINETPAY_APIKEY=
CINETPAY_SITE_ID=
```

- [ ] **Step 3 : Configurer la base de test**

Créer/compléter `.env.test` avec une base dédiée :
```dotenv
DATABASE_URL="mysql://root:@127.0.0.1:3306/cartecadeau_test?serverVersion=<VERSION>&charset=utf8mb4"
```

- [ ] **Step 4 : Créer les bases dev et test**

Run :
```bash
php bin/console doctrine:database:create
php bin/console doctrine:database:create --env=test
```
Expected : `Created database ... cartecadeau` et `cartecadeau_test` (ou « already exists » si déjà créées).

- [ ] **Step 5 : Vérifier la connexion**

Run :
```bash
php bin/console dbal:run-sql "SELECT 1 AS ok"
```
Expected : tableau avec `ok = 1`.

- [ ] **Step 6 : Commit**

```bash
git add .env.test
git commit -m "chore: configuration base de données MySQL (dev + test)"
```
Note : `.env.local` est git-ignoré par défaut — ne pas le committer.

---

## Task 3 : Layout « Neon Nocturne » + design tokens CSS

**Files:**
- Modify: `templates/base.html.twig`
- Create: `assets/styles/app.css` (remplace le contenu par défaut)

- [ ] **Step 1 : Écrire les design tokens et composants dans `assets/styles/app.css`**

Remplacer le contenu de `assets/styles/app.css` par :
```css
:root {
  --bg-0: #0B0B14;
  --bg-1: #12121F;
  --bg-2: #1B1B2E;
  --txt-0: #ECECF5;
  --txt-1: #A9A9C2;
  --violet: #7C3AED;
  --cyan: #22D3EE;
  --grad: linear-gradient(135deg, #7C3AED 0%, #22D3EE 100%);
  --radius: 16px;
  --glass-bg: rgba(255, 255, 255, 0.04);
  --glass-brd: rgba(255, 255, 255, 0.10);
  --shadow-glow: 0 8px 32px rgba(124, 58, 237, 0.25);
}

* { box-sizing: border-box; }
body {
  margin: 0;
  background:
    radial-gradient(1200px 600px at 80% -10%, rgba(124,58,237,0.18), transparent 60%),
    radial-gradient(900px 500px at 0% 10%, rgba(34,211,238,0.12), transparent 55%),
    var(--bg-0);
  color: var(--txt-0);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  min-height: 100vh;
}
a { color: var(--cyan); text-decoration: none; }

.navbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 32px;
  border-bottom: 1px solid var(--glass-brd);
  backdrop-filter: blur(8px);
}
.brand {
  font-weight: 800; font-size: 1.35rem; letter-spacing: 0.5px;
  background: var(--grad); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.nav-links { display: flex; gap: 20px; align-items: center; }

.container { max-width: 1100px; margin: 0 auto; padding: 48px 24px; }

.hero { text-align: center; padding: 64px 0; }
.hero h1 { font-size: clamp(2rem, 5vw, 3.4rem); margin: 0 0 16px; line-height: 1.1; }
.hero .accent { background: var(--grad); -webkit-background-clip: text; background-clip: text; color: transparent; }
.hero p { color: var(--txt-1); font-size: 1.15rem; max-width: 560px; margin: 0 auto 32px; }

.card {
  background: var(--glass-bg);
  border: 1px solid var(--glass-brd);
  border-radius: var(--radius);
  padding: 24px;
  backdrop-filter: blur(12px);
  box-shadow: var(--shadow-glow);
}
.grid { display: grid; gap: 20px; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

.btn {
  display: inline-block; cursor: pointer; border: none;
  padding: 13px 26px; border-radius: 999px; font-weight: 700; font-size: 1rem;
  background: var(--grad); color: #0B0B14;
  box-shadow: var(--shadow-glow); transition: transform .12s ease, filter .12s ease;
}
.btn:hover { transform: translateY(-1px); filter: brightness(1.08); }
.btn-ghost {
  background: transparent; color: var(--txt-0);
  border: 1px solid var(--glass-brd); box-shadow: none;
}

.form-wrap { max-width: 420px; margin: 48px auto; }
.form-wrap label { display: block; margin: 14px 0 6px; color: var(--txt-1); font-size: .92rem; }
.form-wrap input {
  width: 100%; padding: 12px 14px; border-radius: 12px;
  background: var(--bg-1); border: 1px solid var(--glass-brd); color: var(--txt-0);
}
.form-wrap input:focus { outline: 2px solid var(--violet); }
.alert { padding: 12px 16px; border-radius: 12px; margin: 12px 0; }
.alert-error { background: rgba(239,68,68,.15); border: 1px solid rgba(239,68,68,.4); }
.flash-success { background: rgba(34,211,238,.12); border: 1px solid rgba(34,211,238,.4); padding: 12px 16px; border-radius: 12px; }
```

- [ ] **Step 2 : Écrire le layout `templates/base.html.twig`**

Remplacer le contenu par :
```twig
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}CARTECADEAU{% endblock %}</title>
    {% block stylesheets %}
        {{ importmap('app') }}
    {% endblock %}
</head>
<body>
    <nav class="navbar">
        <a href="{{ path('app_home') }}" class="brand">CARTECADEAU</a>
        <div class="nav-links">
            {% if app.user %}
                <span>{{ app.user.userIdentifier }}</span>
                <a href="{{ path('app_logout') }}" class="btn-ghost btn">Déconnexion</a>
            {% else %}
                <a href="{{ path('app_login') }}">Connexion</a>
                <a href="{{ path('app_register') }}" class="btn">Créer un compte</a>
            {% endif %}
        </div>
    </nav>
    <main>
        {% for message in app.flashes('success') %}
            <div class="container"><div class="flash-success">{{ message }}</div></div>
        {% endfor %}
        {% block body %}{% endblock %}
    </main>
</body>
</html>
```

- [ ] **Step 3 : Vérifier la compilation des assets**

Run :
```bash
php bin/console asset-map:compile
```
Expected : compilation sans erreur (fichiers dans `public/assets/`). (On peut nettoyer ensuite, le serveur dev sert les assets à la volée.)

- [ ] **Step 4 : Commit**

```bash
git add assets templates/base.html.twig
git commit -m "feat: layout et design tokens Neon Nocturne"
```

---

## Task 4 : Page d'accueil (test-first)

**Files:**
- Create: `tests/Controller/HomeControllerTest.php`
- Create: `src/Controller/HomeController.php`
- Create: `templates/home/index.html.twig`

- [ ] **Step 1 : Écrire le test fonctionnel (échoue)**

Créer `tests/Controller/HomeControllerTest.php` :
```php
<?php
namespace App\Tests\Controller;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class HomeControllerTest extends WebTestCase
{
    public function testHomePageLoads(): void
    {
        $client = static::createClient();
        $client->request('GET', '/');

        self::assertResponseIsSuccessful();
        self::assertSelectorTextContains('.brand', 'CARTECADEAU');
        self::assertSelectorExists('.btn');
    }
}
```

- [ ] **Step 2 : Lancer le test pour vérifier l'échec**

Run :
```bash
php bin/phpunit tests/Controller/HomeControllerTest.php
```
Expected : FAIL (route `/` inexistante → 404).

- [ ] **Step 3 : Créer le contrôleur**

Créer `src/Controller/HomeController.php` :
```php
<?php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class HomeController extends AbstractController
{
    #[Route('/', name: 'app_home')]
    public function index(): Response
    {
        return $this->render('home/index.html.twig');
    }
}
```

- [ ] **Step 4 : Créer la vue**

Créer `templates/home/index.html.twig` :
```twig
{% extends 'base.html.twig' %}
{% block title %}CARTECADEAU — Cartes-cadeaux & recharges{% endblock %}
{% block body %}
<section class="container hero">
    <h1>Vos <span class="accent">cartes-cadeaux</span> et <span class="accent">recharges de jeux</span><br>en quelques secondes</h1>
    <p>Apple, Google Play, PUBG et bien plus. Paiement Mobile Money, livraison instantanée de vos codes.</p>
    <a href="{{ path('app_register') }}" class="btn">Commencer</a>
</section>
<section class="container">
    <div class="grid">
        <div class="card"><h3>Cartes-cadeaux</h3><p>Recevez vos codes/PIN immédiatement après paiement.</p></div>
        <div class="card"><h3>Recharges de jeux</h3><p>Top-up direct sur votre compte joueur.</p></div>
        <div class="card"><h3>Mobile Money</h3><p>Wave, Orange, MTN, Moov via CinetPay.</p></div>
    </div>
</section>
{% endblock %}
```

- [ ] **Step 5 : Lancer le test pour vérifier le succès**

Run :
```bash
php bin/phpunit tests/Controller/HomeControllerTest.php
```
Expected : PASS (1 test, 3 assertions).

- [ ] **Step 6 : Commit**

```bash
git add src/Controller/HomeController.php templates/home tests/Controller/HomeControllerTest.php
git commit -m "feat: page d'accueil Neon Nocturne (test-first)"
```

---

## Task 5 : Entité User + inscription (test-first)

**Files:**
- Create: `src/Entity/User.php`, `src/Repository/UserRepository.php` (via maker)
- Create: `src/Form/RegistrationFormType.php`, `src/Controller/RegistrationController.php` (via maker)
- Create: `templates/registration/register.html.twig`
- Create: `tests/Controller/RegistrationControllerTest.php`
- Create: `migrations/VersionXXXX.php`

- [ ] **Step 1 : Générer l'entité User via maker**

Run :
```bash
php bin/console make:user
```
Réponses : nom de classe `User` ; stocker en base `yes` ; propriété d'identification `email` ; hasher les mots de passe `yes`.
Expected : `src/Entity/User.php` et `src/Repository/UserRepository.php` créés ; `config/packages/security.yaml` mis à jour avec le provider et le hasher.

- [ ] **Step 2 : Générer le formulaire d'inscription via maker**

Run :
```bash
php bin/console make:registration-form
```
Réponses : pas de vérification d'email par lien `no` (v1) ; authentifier automatiquement après inscription `no` ; route de redirection après inscription `app_home`.
Expected : `src/Form/RegistrationFormType.php`, `src/Controller/RegistrationController.php`, `templates/registration/register.html.twig` créés.

- [ ] **Step 3 : Styliser le template d'inscription**

Remplacer le contenu de `templates/registration/register.html.twig` par :
```twig
{% extends 'base.html.twig' %}
{% block title %}Créer un compte — CARTECADEAU{% endblock %}
{% block body %}
<div class="container">
  <div class="card form-wrap">
    <h1>Créer un compte</h1>
    {{ form_start(registrationForm) }}
        <label>{{ form_label(registrationForm.email) }}</label>
        {{ form_widget(registrationForm.email) }}
        <div class="alert-error">{{ form_errors(registrationForm.email) }}</div>
        <label>{{ form_label(registrationForm.plainPassword) }}</label>
        {{ form_widget(registrationForm.plainPassword) }}
        <div class="alert-error">{{ form_errors(registrationForm.plainPassword) }}</div>
        <label>{{ form_label(registrationForm.agreeTerms) }}</label>
        {{ form_widget(registrationForm.agreeTerms) }}
        <div style="margin-top:20px">{{ form_widget(registrationForm.submit ?? '') }}<button class="btn" type="submit">S'inscrire</button></div>
    {{ form_end(registrationForm) }}
    <p style="margin-top:16px">Déjà un compte ? <a href="{{ path('app_login') }}">Se connecter</a></p>
  </div>
</div>
{% endblock %}
```

- [ ] **Step 4 : Créer et appliquer la migration**

Run :
```bash
php bin/console make:migration
php bin/console doctrine:migrations:migrate --no-interaction
php bin/console doctrine:migrations:migrate --no-interaction --env=test
```
Expected : table `user` créée dans `cartecadeau` et `cartecadeau_test`.

- [ ] **Step 5 : Écrire le test fonctionnel d'inscription**

Créer `tests/Controller/RegistrationControllerTest.php` :
```php
<?php
namespace App\Tests\Controller;

use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class RegistrationControllerTest extends WebTestCase
{
    public function testRegisterCreatesUser(): void
    {
        $client = static::createClient();
        $repo = static::getContainer()->get(UserRepository::class);
        foreach ($repo->findBy(['email' => 'test@cartecadeau.test']) as $u) {
            static::getContainer()->get('doctrine')->getManager()->remove($u);
        }
        static::getContainer()->get('doctrine')->getManager()->flush();

        $client->request('GET', '/register');
        self::assertResponseIsSuccessful();

        $client->submitForm('S\'inscrire', [
            'registration_form[email]' => 'test@cartecadeau.test',
            'registration_form[plainPassword]' => 'Sup3rSecret!',
            'registration_form[agreeTerms]' => true,
        ]);

        self::assertResponseRedirects();
        self::assertNotNull($repo->findOneBy(['email' => 'test@cartecadeau.test']));
    }
}
```
Note : si les noms de champs générés diffèrent (`registration_form[...]`), les ajuster d'après `register.html.twig`.

- [ ] **Step 6 : Lancer le test**

Run :
```bash
php bin/phpunit tests/Controller/RegistrationControllerTest.php
```
Expected : PASS. Si le bouton submit n'est pas trouvé, vérifier le libellé exact dans le template et réaligner.

- [ ] **Step 7 : Commit**

```bash
git add src templates/registration migrations tests config/packages/security.yaml
git commit -m "feat: entité User et inscription client (test-first)"
```

---

## Task 6 : Connexion / déconnexion + contrôle d'accès (test-first)

**Files:**
- Create: `src/Controller/SecurityController.php`, `templates/security/login.html.twig` (via maker)
- Modify: `config/packages/security.yaml`
- Create: `tests/Controller/SecurityControllerTest.php`

- [ ] **Step 1 : Générer le formulaire de connexion via maker**

Run :
```bash
php bin/console make:security:form-login
```
Réponses : générer le contrôleur `SecurityController` `yes` ; route de logout `yes` ; ajouter `?` selon prompts.
Expected : `src/Controller/SecurityController.php` (routes `app_login`, `app_logout`), `templates/security/login.html.twig`, `security.yaml` mis à jour (`form_login`, `logout`).

- [ ] **Step 2 : Styliser le template de connexion**

Remplacer le contenu de `templates/security/login.html.twig` par :
```twig
{% extends 'base.html.twig' %}
{% block title %}Connexion — CARTECADEAU{% endblock %}
{% block body %}
<div class="container">
  <div class="card form-wrap">
    <h1>Connexion</h1>
    {% if error %}<div class="alert alert-error">{{ error.messageKey|trans(error.messageData, 'security') }}</div>{% endif %}
    <form method="post">
        <label for="username">Email</label>
        <input type="email" id="username" name="_username" value="{{ last_username }}" required autofocus>
        <label for="password">Mot de passe</label>
        <input type="password" id="password" name="_password" required>
        <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
        <div style="margin-top:20px"><button class="btn" type="submit">Se connecter</button></div>
    </form>
    <p style="margin-top:16px">Pas encore de compte ? <a href="{{ path('app_register') }}">Créer un compte</a></p>
  </div>
</div>
{% endblock %}
```
Note : si maker a généré des noms de champ différents (ex. `email`/`password`), aligner le template ET `security.yaml` (`form_login` : `username_parameter`, `password_parameter`).

- [ ] **Step 3 : Protéger l'espace client dans `security.yaml`**

Dans `config/packages/security.yaml`, sous `access_control:`, ajouter (créer la section si absente) :
```yaml
    access_control:
        - { path: ^/compte, roles: ROLE_USER }
```

- [ ] **Step 4 : Écrire le test connexion + contrôle d'accès**

Créer `tests/Controller/SecurityControllerTest.php` :
```php
<?php
namespace App\Tests\Controller;

use App\Entity\User;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;

class SecurityControllerTest extends WebTestCase
{
    public function testLoginPageLoads(): void
    {
        $client = static::createClient();
        $client->request('GET', '/login');
        self::assertResponseIsSuccessful();
        self::assertSelectorExists('input[name="_password"]');
    }

    public function testProtectedAreaRedirectsAnonymous(): void
    {
        $client = static::createClient();
        $client->request('GET', '/compte');
        self::assertResponseRedirects('/login');
    }

    public function testUserCanLogin(): void
    {
        $client = static::createClient();
        $container = static::getContainer();
        $em = $container->get('doctrine')->getManager();
        $hasher = $container->get(UserPasswordHasherInterface::class);

        $email = 'login@cartecadeau.test';
        foreach ($em->getRepository(User::class)->findBy(['email' => $email]) as $u) { $em->remove($u); }
        $em->flush();

        $user = new User();
        $user->setEmail($email);
        $user->setPassword($hasher->hashPassword($user, 'Sup3rSecret!'));
        $em->persist($user);
        $em->flush();

        $client->request('GET', '/login');
        $client->submitForm('Se connecter', [
            '_username' => $email,
            '_password' => 'Sup3rSecret!',
        ]);
        self::assertResponseRedirects();
    }
}
```
Note : si Task 6/Step 2 utilise d'autres `name=` de champs, ajuster les clés du `submitForm`.

- [ ] **Step 5 : Lancer toute la suite**

Run :
```bash
php bin/phpunit
```
Expected : tous les tests PASS (Home, Registration, Security).

- [ ] **Step 6 : Commit**

```bash
git add src/Controller/SecurityController.php templates/security tests/Controller/SecurityControllerTest.php config/packages/security.yaml
git commit -m "feat: connexion/déconnexion et contrôle d'accès espace client (test-first)"
```

---

## Task 7 : Vérification manuelle du rendu

- [ ] **Step 1 : Démarrer le serveur et vérifier visuellement**

Run :
```bash
symfony serve -d
```
Puis ouvrir `https://127.0.0.1:8000/` : vérifier le thème sombre, le dégradé violet→cyan du titre/bouton, les cartes en verre dépoli. Tester `/register` puis `/login`.
Expected : design Neon Nocturne cohérent, parcours inscription→connexion fonctionnel.

- [ ] **Step 2 : Arrêter le serveur**

Run :
```bash
symfony server:stop
```

---

## Self-Review (rempli par l'auteur du plan)

**Couverture de la spec (Lot 1) :**
- Stack Symfony + MySQL → Tasks 1–2. ✅
- Comptes clients obligatoires (inscription/connexion/contrôle d'accès) → Tasks 5–6. ✅
- Design Neon Nocturne (tokens, glassmorphism, dégradé) → Tasks 3–4, vérif Task 7. ✅
- Secrets dans `.env.local` (git-ignoré, non commité) → Task 2 (note explicite). ✅
- Hors Lot 1 (couverts par lots suivants) : WupexClient/catalogue (Lot 2), panier/checkout (Lot 3), CinetPay (Lot 4), livraison async (Lot 5), admin (Lot 6).

**Scan placeholders :** Les `<VERSION>` (Task 2) et les notes « ajuster si maker génère d'autres noms » sont des actions concrètes dépendantes de l'environnement/maker, pas des TODO ouverts. Aucun « TBD/à implémenter plus tard ».

**Cohérence des types/noms :** `User` (email, password, plainPassword), routes `app_home`, `app_register`, `app_login`, `app_logout`, classe de form `registration_form`, champs login `_username`/`_password` — utilisés de façon cohérente entre tasks et tests.

**Dépendances :** Task 4 dépend des routes `app_register`/`app_login` (créées en Tasks 5–6). Le test Home n'asserte que `.brand` et `.btn` (présents dès Task 3/4), donc il passe avant l'existence des routes auth car le lien hero pointe vers `app_register` qui sera créé en Task 5 — ⚠️ exécuter les tasks dans l'ordre ; le rendu de `home/index.html.twig` appelle `path('app_register')`. **Correction : voir note ci-dessous.**

> ⚠️ **Note d'ordre d'exécution :** `templates/home/index.html.twig` (Task 4) et `base.html.twig` (Task 3) référencent `path('app_register')`, `path('app_login')`, `path('app_logout')` qui n'existent qu'après Tasks 5–6. Twig lève une erreur si une route est inconnue au rendu. **Donc : exécuter Task 5 et Task 6 AVANT de lancer le test de Task 4** (ou, plus simple, lancer le test de Task 4 après Task 6). L'ordre recommandé d'exécution réelle est : Task 1 → 2 → 3 → 5 → 6 → 4 → 7. Les commits restent par task.
