---
name: feature-full
description: |
  Cree une feature complete SmartStack avec toutes les integrations.
  Utiliser ce skill quand:
  - L'utilisateur veut creer une feature complete de A a Z
  - L'utilisateur mentionne "oneshot", "feature complete", "full-stack"
  - Creation d'un module avec notifications, workflows et/ou IA
  - Besoin d'une experience utilisateur complete
  Scope: Domain → Application → Infrastructure → API → Web + Notifications + Workflows + AI
---

# Skill Feature Full SmartStack

> **OneShot Development:** Ce skill orchestre tous les autres skills pour creer
> une feature complete avec la meilleure experience utilisateur possible.

## QUAND CE SKILL S'ACTIVE

Claude invoque automatiquement ce skill quand il detecte :

| Declencheur | Exemple |
|-------------|---------|
| Feature complete | "Cree un module de gestion des produits" |
| OneShot | "Implemente la feature de A a Z" |
| Full-stack | "Cree le backend et le frontend pour..." |
| UX focus | "Je veux une experience utilisateur complete" |
| Integration | "Avec notifications et emails automatiques" |

---

## ARCHITECTURE FEATURE COMPLETE

```
┌─────────────────────────────────────────────────────────────────────────────┐
│                      FEATURE FULL-STACK COMPLETE                            │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │                          DOMAIN LAYER                                 │  │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐     │  │
│  │  │ Entity  │  │ Enums   │  │ Events  │  │ Factory │  │ Specs   │     │  │
│  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘  └─────────┘     │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │                        APPLICATION LAYER                              │  │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐                  │  │
│  │  │Interface│  │  DTOs   │  │Commands │  │Validators│                  │  │
│  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘                  │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │                      INFRASTRUCTURE LAYER                             │  │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐     │  │
│  │  │ EF Core │  │ Service │  │  Seed   │  │ SignalR │  │ Email   │     │  │
│  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘  └─────────┘     │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │                           API LAYER                                   │  │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐                  │  │
│  │  │Controller│ │Authorize │  │ Swagger │  │Postman  │                  │  │
│  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘                  │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │                           WEB LAYER                                   │  │
│  │  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐  ┌─────────┐     │  │
│  │  │  Pages  │  │ Hooks   │  │   i18n  │  │  API    │  │ Routes  │     │  │
│  │  └─────────┘  └─────────┘  └─────────┘  └─────────┘  └─────────┘     │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                                    │                                        │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │                        INTEGRATIONS                                   │  │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                   │  │
│  │  │NOTIFICATIONS│  │  WORKFLOWS  │  │     AI      │                   │  │
│  │  │  In-App     │  │   Emails    │  │  Analysis   │                   │  │
│  │  │  Real-time  │  │  Webhooks   │  │  Generation │                   │  │
│  │  └─────────────┘  └─────────────┘  └─────────────┘                   │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘
```

---

## WORKFLOW DE CREATION

### PHASE 1: ANALYSE (5 min)

```
1. IDENTIFIER LE BESOIN
   □ Quel module/fonctionnalite ?
   □ Quelles entites ?
   □ Quelles operations CRUD ?
   □ Quelles regles metier ?

2. DEFINIR LES INTEGRATIONS
   □ Notifications requises ?
     → Quand ? Pour qui ? Quel type ?
   □ Emails automatiques ?
     → Quels triggers ? Quels templates ?
   □ Assistance IA ?
     → Quel use case ? Quelle validation ?

3. DEFINIR L'UX
   □ Affichage: Cards, Table, Kanban ?
   □ Navigation: Ou dans l'arborescence ?
   □ Permissions: Qui peut faire quoi ?
```

### PHASE 2: GENERATION DOMAIN (10 min)

```csharp
// 1. ENTITE DOMAIN
// src/SmartStack.Domain/{Area}/{Entity}.cs

public class {Entity} : BaseEntity, IAuditableEntity
{
    // Proprietes
    public string Name { get; private set; }
    public string? Description { get; private set; }
    public {Entity}Status Status { get; private set; }

    // Relations
    public Guid CreatedById { get; private set; }
    public User CreatedBy { get; private set; } = null!;

    // Audit
    public string? CreatedByName { get; set; }
    public string? UpdatedByName { get; set; }

    // Private constructor for EF Core
    private {Entity}() { }

    // Factory Method
    public static {Entity} Create(
        string name,
        string? description,
        Guid createdById)
    {
        if (string.IsNullOrWhiteSpace(name))
            throw new DomainException("{Entity} name is required");

        return new {Entity}
        {
            Id = Guid.NewGuid(),
            Name = name,
            Description = description,
            Status = {Entity}Status.Active,
            CreatedById = createdById,
            CreatedAt = DateTime.UtcNow
        };
    }

    // Behaviors
    public void Update(string name, string? description)
    {
        Name = name ?? throw new DomainException("Name is required");
        Description = description;
        UpdatedAt = DateTime.UtcNow;
    }

    public void Activate() => Status = {Entity}Status.Active;
    public void Deactivate() => Status = {Entity}Status.Inactive;
}

// 2. ENUM
// src/SmartStack.Domain/{Area}/Enums/{Entity}Status.cs

public enum {Entity}Status
{
    Active = 0,
    Inactive = 1,
    Archived = 2
}
```

### PHASE 3: GENERATION APPLICATION (10 min)

```csharp
// 1. INTERFACE SERVICE
// src/SmartStack.Application/Common/Interfaces/I{Entity}Service.cs

public interface I{Entity}Service
{
    Task<PagedResult<{Entity}Dto>> GetAllAsync(
        int page, int pageSize, string? search, CancellationToken ct);
    Task<{Entity}Dto?> GetByIdAsync(Guid id, CancellationToken ct);
    Task<{Entity}Dto> CreateAsync(Create{Entity}Request request, CancellationToken ct);
    Task<{Entity}Dto> UpdateAsync(Guid id, Update{Entity}Request request, CancellationToken ct);
    Task DeleteAsync(Guid id, CancellationToken ct);
}

// 2. DTOs
// src/SmartStack.Application/{Area}/DTOs/{Entity}Dto.cs

public record {Entity}Dto(
    Guid Id,
    string Name,
    string? Description,
    string Status,
    DateTime CreatedAt,
    string? CreatedByName);

public record Create{Entity}Request(
    string Name,
    string? Description);

public record Update{Entity}Request(
    string Name,
    string? Description);

// 3. PERMISSIONS
// src/SmartStack.Application/Common/Authorization/Permissions.cs

public static class {Area}
{
    public static class {Module}
    {
        public const string View = "{context}.{area}.{module}.read";
        public const string Create = "{context}.{area}.{module}.create";
        public const string Update = "{context}.{area}.{module}.update";
        public const string Delete = "{context}.{area}.{module}.delete";
    }
}
```

### PHASE 4: GENERATION INFRASTRUCTURE (15 min)

```csharp
// 1. CONFIGURATION EF CORE
// src/SmartStack.Infrastructure/Persistence/Configurations/{Area}/{Entity}Configuration.cs

public class {Entity}Configuration : IEntityTypeConfiguration<{Entity}>
{
    public void Configure(EntityTypeBuilder<{Entity}> builder)
    {
        builder.ToTable("{Entity}s", "{schema}");

        builder.HasKey(x => x.Id);

        builder.Property(x => x.Name)
            .HasMaxLength(200)
            .IsRequired();

        builder.Property(x => x.Description)
            .HasMaxLength(2000);

        builder.HasIndex(x => x.Name);
        builder.HasIndex(x => x.Status);
        builder.HasIndex(x => x.CreatedById);

        // Navigation
        builder.HasOne(x => x.CreatedBy)
            .WithMany()
            .HasForeignKey(x => x.CreatedById)
            .OnDelete(DeleteBehavior.Restrict);
    }
}

// 2. SERVICE IMPLEMENTATION
// src/SmartStack.Infrastructure/Services/{Area}/{Entity}Service.cs

public class {Entity}Service : I{Entity}Service
{
    private readonly IApplicationDbContext _context;
    private readonly ICurrentUserService _currentUser;
    private readonly INotificationService _notificationService;
    private readonly ILogger<{Entity}Service> _logger;

    public {Entity}Service(
        IApplicationDbContext context,
        ICurrentUserService currentUser,
        INotificationService notificationService,
        ILogger<{Entity}Service> logger)
    {
        _context = context;
        _currentUser = currentUser;
        _notificationService = notificationService;
        _logger = logger;
    }

    public async Task<{Entity}Dto> CreateAsync(
        Create{Entity}Request request,
        CancellationToken ct)
    {
        var entity = {Entity}.Create(
            request.Name,
            request.Description,
            _currentUser.Id);

        entity.CreatedByName = _currentUser.DisplayName;

        _context.{Entity}s.Add(entity);
        await _context.SaveChangesAsync(ct);

        // NOTIFICATION
        await _notificationService.SendNotificationAsync(
            _currentUser.Id,
            NotificationType.{Entity}Created,
            "{Entity} creee",
            $"{entity.Name} a ete cree avec succes",
            relatedEntityType: nameof({Entity}),
            relatedEntityId: entity.Id,
            actionUrl: $"/{area}/{module}/{entity.Id}",
            cancellationToken: ct);

        _logger.LogInformation(
            "User {UserId} created {EntityType} {EntityId}: {EntityName}",
            _currentUser.Id, nameof({Entity}), entity.Id, entity.Name);

        return MapToDto(entity);
    }

    // ... autres methodes CRUD avec notifications
}

// 3. DEPENDENCY INJECTION
// Ajouter dans DependencyInjection.cs:
services.AddScoped<I{Entity}Service, {Entity}Service>();
```

### PHASE 5: GENERATION API (10 min)

```csharp
// src/SmartStack.Api/Controllers/{Area}/{Entity}Controller.cs

[ApiController]
[Route("api/{area}/{module}")]
[Authorize]
public class {Entity}Controller : ControllerBase
{
    private readonly I{Entity}Service _service;
    private readonly ILogger<{Entity}Controller> _logger;

    public {Entity}Controller(
        I{Entity}Service service,
        ILogger<{Entity}Controller> logger)
    {
        _service = service;
        _logger = logger;
    }

    [HttpGet]
    [RequirePermission(Permissions.{Area}.{Module}.View)]
    [ProducesResponseType(typeof(PagedResult<{Entity}Dto>), StatusCodes.Status200OK)]
    public async Task<ActionResult<PagedResult<{Entity}Dto>>> GetAll(
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 20,
        [FromQuery] string? search = null,
        CancellationToken ct = default)
    {
        var result = await _service.GetAllAsync(page, pageSize, search, ct);
        return Ok(result);
    }

    [HttpGet("{id:guid}")]
    [RequirePermission(Permissions.{Area}.{Module}.View)]
    [ProducesResponseType(typeof({Entity}Dto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<{Entity}Dto>> GetById(Guid id, CancellationToken ct)
    {
        var result = await _service.GetByIdAsync(id, ct);
        return result is null ? NotFound() : Ok(result);
    }

    [HttpPost]
    [RequirePermission(Permissions.{Area}.{Module}.Create)]
    [ProducesResponseType(typeof({Entity}Dto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<ActionResult<{Entity}Dto>> Create(
        [FromBody] Create{Entity}Request request,
        CancellationToken ct)
    {
        var result = await _service.CreateAsync(request, ct);
        return CreatedAtAction(nameof(GetById), new { id = result.Id }, result);
    }

    [HttpPut("{id:guid}")]
    [RequirePermission(Permissions.{Area}.{Module}.Update)]
    [ProducesResponseType(typeof({Entity}Dto), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<ActionResult<{Entity}Dto>> Update(
        Guid id,
        [FromBody] Update{Entity}Request request,
        CancellationToken ct)
    {
        var result = await _service.UpdateAsync(id, request, ct);
        return Ok(result);
    }

    [HttpDelete("{id:guid}")]
    [RequirePermission(Permissions.{Area}.{Module}.Delete)]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> Delete(Guid id, CancellationToken ct)
    {
        await _service.DeleteAsync(id, ct);
        return NoContent();
    }
}
```

### PHASE 6: GENERATION FRONTEND (20 min)

```typescript
// 1. API SERVICE
// web/src/services/api/{module}Api.ts

import { apiClient } from './apiClient';
import type { PagedResult, {Entity}Dto, Create{Entity}Request, Update{Entity}Request } from '@/types';

export const {module}Api = {
  getAll: (page = 1, pageSize = 20, search?: string) =>
    apiClient.get<PagedResult<{Entity}Dto>>('/{area}/{module}', {
      params: { page, pageSize, search }
    }),

  getById: (id: string) =>
    apiClient.get<{Entity}Dto>(`/{area}/{module}/${id}`),

  create: (data: Create{Entity}Request) =>
    apiClient.post<{Entity}Dto>('/{area}/{module}', data),

  update: (id: string, data: Update{Entity}Request) =>
    apiClient.put<{Entity}Dto>(`/{area}/{module}/${id}`, data),

  delete: (id: string) =>
    apiClient.delete(`/{area}/{module}/${id}`),
};

// 2. HOOK AVEC SIGNALR
// web/src/hooks/use{Module}.ts

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { {module}Api } from '@/services/api/{module}Api';
import { useSignalR } from '@/hooks/useSignalR';
import { toast } from 'sonner';

export function use{Module}() {
  const queryClient = useQueryClient();

  // Real-time updates
  useSignalR({
    onNotification: (notification) => {
      if (notification.relatedEntityType === '{Entity}') {
        queryClient.invalidateQueries(['{module}']);
        toast.info(notification.title, {
          description: notification.message,
        });
      }
    },
  });

  const {module}Query = useQuery({
    queryKey: ['{module}'],
    queryFn: () => {module}Api.getAll(),
  });

  const createMutation = useMutation({
    mutationFn: {module}Api.create,
    onSuccess: () => {
      queryClient.invalidateQueries(['{module}']);
      toast.success('{Entity} creee');
    },
    onError: (error) => {
      toast.error('Erreur lors de la creation');
    },
  });

  const updateMutation = useMutation({
    mutationFn: ({ id, data }: { id: string; data: Update{Entity}Request }) =>
      {module}Api.update(id, data),
    onSuccess: () => {
      queryClient.invalidateQueries(['{module}']);
      toast.success('{Entity} mise a jour');
    },
  });

  const deleteMutation = useMutation({
    mutationFn: {module}Api.delete,
    onSuccess: () => {
      queryClient.invalidateQueries(['{module}']);
      toast.success('{Entity} supprimee');
    },
  });

  return {
    {module}: {module}Query.data?.items ?? [],
    isLoading: {module}Query.isLoading,
    error: {module}Query.error,
    create: createMutation.mutate,
    update: updateMutation.mutate,
    delete: deleteMutation.mutate,
    isCreating: createMutation.isPending,
    isUpdating: updateMutation.isPending,
    isDeleting: deleteMutation.isPending,
  };
}

// 3. PAGE PRINCIPALE
// web/src/pages/{area}/{module}/{Module}Page.tsx

import { useState } from 'react';
import { Plus, Search } from 'lucide-react';
import { use{Module} } from '@/hooks/use{Module}';
import { EntityCard } from '@/components/ui/EntityCard';
import { {Module}Form } from './components/{Module}Form';
import { useTranslation } from 'react-i18next';

export function {Module}Page() {
  const { t } = useTranslation('{module}');
  const { {module}, isLoading, create, delete: remove } = use{Module}();
  const [showForm, setShowForm] = useState(false);
  const [search, setSearch] = useState('');

  const filtered = {module}.filter(item =>
    item.name.toLowerCase().includes(search.toLowerCase())
  );

  return (
    <div className="space-y-6">
      {/* Header */}
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-2xl font-bold">{t('title')}</h1>
          <p className="text-[var(--text-secondary)]">{t('subtitle')}</p>
        </div>
        <button
          onClick={() => setShowForm(true)}
          className="inline-flex items-center gap-2 px-4 py-2 bg-[var(--color-accent-600)] text-white rounded-lg"
        >
          <Plus className="w-4 h-4" />
          {t('create')}
        </button>
      </div>

      {/* Search */}
      <div className="relative">
        <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--text-tertiary)]" />
        <input
          type="text"
          placeholder={t('search')}
          value={search}
          onChange={(e) => setSearch(e.target.value)}
          className="w-full pl-10 pr-4 py-2 border rounded-lg"
        />
      </div>

      {/* Grid */}
      {isLoading ? (
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
          {Array.from({ length: 6 }).map((_, i) => (
            <div key={i} className="h-48 bg-[var(--bg-secondary)] animate-pulse rounded-lg" />
          ))}
        </div>
      ) : filtered.length === 0 ? (
        <div className="text-center py-12">
          <p className="text-[var(--text-secondary)]">{t('empty')}</p>
        </div>
      ) : (
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
          {filtered.map((item) => (
            <EntityCard
              key={item.id}
              avatar={{ letter: item.name[0], color: 'var(--color-accent-500)' }}
              title={item.name}
              subtitle={item.status}
              description={item.description}
              actions={[
                { label: t('view'), onClick: () => navigate(`/${area}/{module}/${item.id}`), variant: 'primary' },
                { label: t('delete'), onClick: () => remove(item.id), variant: 'ghost' },
              ]}
            />
          ))}
        </div>
      )}

      {/* Form Modal */}
      {showForm && (
        <{Module}Form
          onSubmit={(data) => {
            create(data);
            setShowForm(false);
          }}
          onClose={() => setShowForm(false)}
        />
      )}
    </div>
  );
}

// 4. i18n
// web/src/i18n/locales/fr/{module}.json

{
  "title": "{Module}",
  "subtitle": "Gerez vos {module}s",
  "create": "Creer",
  "search": "Rechercher...",
  "empty": "Aucun {module} trouve",
  "view": "Voir",
  "edit": "Modifier",
  "delete": "Supprimer",
  "form": {
    "name": "Nom",
    "namePlaceholder": "Entrez le nom",
    "description": "Description",
    "descriptionPlaceholder": "Entrez une description",
    "submit": "Enregistrer",
    "cancel": "Annuler"
  },
  "notifications": {
    "created": "{Module} creee avec succes",
    "updated": "{Module} mise a jour",
    "deleted": "{Module} supprimee"
  }
}

// 5. ROUTES
// App.tsx - Ajouter:
<Route path="{area}">
  <Route index element={<Navigate to="{module}" replace />} />
  <Route path="{module}">
    <Route index element={<{Module}Page />} />
    <Route path=":id" element={<{Module}DetailPage />} />
  </Route>
</Route>
```

### PHASE 7: INTEGRATIONS (15 min)

#### 7.1 Notifications

```csharp
// Dans le service, ajouter les notifications appropriees:

// Creation
await _notificationService.SendNotificationAsync(
    _currentUser.Id,
    NotificationType.{Entity}Created,
    t["notifications.created.title"],
    t["notifications.created.message", entity.Name],
    relatedEntityType: nameof({Entity}),
    relatedEntityId: entity.Id,
    actionUrl: $"/{area}/{module}/{entity.Id}");

// Si assignation
if (entity.AssignedToId.HasValue)
{
    await _notificationService.SendNotificationAsync(
        entity.AssignedToId.Value,
        NotificationType.{Entity}Assigned,
        "{Entity} assignee",
        $"La {entity.Name} vous a ete assignee",
        relatedEntityType: nameof({Entity}),
        relatedEntityId: entity.Id,
        actionUrl: $"/{area}/{module}/{entity.Id}");
}
```

#### 7.2 Workflow Email

```csharp
// 1. Creer le trigger
// Dans WorkflowTriggerConfiguration.cs:
new
{
    Id = Guid.Parse("NEW-TRIGGER-GUID"),
    Code = "{entity}.created",
    Name = "{Entity} Created",
    AvailableVariablesJson = "[{\"Name\":\"entityId\"},{\"Name\":\"entityName\"},{\"Name\":\"creatorEmail\"}]",
    IsActive = true,
    CreatedAt = seedDate
},

// 2. Creer le workflow
// Dans WorkflowConfiguration.cs:
new
{
    Id = Guid.Parse("NEW-WORKFLOW-GUID"),
    Code = "{entity}-created-notification",
    Name = "{Entity} Created Notification",
    TriggerId = Guid.Parse("TRIGGER-GUID"),
    IsActive = true,
    IsSystem = true,
    Priority = 10,
    CreatedAt = seedDate
},

// 3. Declencher dans le service
await _workflowService.TriggerAsync(
    "{entity}.created",
    new Dictionary<string, object>
    {
        ["entityId"] = entity.Id,
        ["entityName"] = entity.Name,
        ["creatorEmail"] = _currentUser.Email
    });
```

#### 7.3 AI Integration

```csharp
// Si analyse IA requise:
public async Task<{Entity}AnalysisResult?> AnalyzeAsync(Guid entityId, CancellationToken ct)
{
    var entity = await _context.{Entity}s.FindAsync(entityId);
    if (entity == null) return null;

    var result = await _aiCompletionService
        .ExecutePromptByCodeWithValidationAsync<{Entity}AnalysisResult>(
            "{entity}-analyzer",
            new Dictionary<string, object>
            {
                ["entityName"] = entity.Name,
                ["entityDescription"] = entity.Description ?? ""
            },
            cancellationToken: ct);

    if (result.Success && result.IsValid)
    {
        _logger.LogInformation(
            "{Entity} {EntityId} analyzed: {Category}",
            entityId, result.Data.Category);
        return result.Data;
    }

    return null;
}
```

---

## CHECKLIST COMPLETE

### Domain Layer
```
□ Entite {Entity}.cs creee
□ Factory Method Create() implemente
□ Behaviors (Update, Activate, etc.)
□ Enum {Entity}Status.cs
□ IAuditableEntity implemente
```

### Application Layer
```
□ Interface I{Entity}Service.cs
□ DTOs: {Entity}Dto, Create/Update Requests
□ Permissions ajoutees dans Permissions.cs
□ Permissions ajoutees dans PermissionConfiguration.cs
```

### Infrastructure Layer
```
□ {Entity}Configuration.cs (EF Core)
□ {Entity}Service.cs implementation
□ Injection DI dans DependencyInjection.cs
□ DbSet<{Entity}> dans ApplicationDbContext
□ Migration EF Core creee
```

### API Layer
```
□ {Entity}Controller.cs
□ [Authorize] et [RequirePermission]
□ [ProducesResponseType] documentes
□ Logging dans les actions
```

### Web Layer
```
□ {module}Api.ts (service API)
□ use{Module}.ts (hook avec SignalR)
□ {Module}Page.tsx (page principale)
□ {Module}Form.tsx (formulaire)
□ {Module}DetailPage.tsx (detail)
□ i18n: fr/{module}.json + en/{module}.json
□ Routes dans App.tsx
```

### Integrations
```
□ Notifications:
  □ NotificationType ajoute
  □ SendNotificationAsync dans le service
  □ useSignalR dans le hook
□ Workflows (si emails requis):
  □ Trigger cree
  □ Workflow cree
  □ TriggerAsync dans le service
□ AI (si analyse requise):
  □ Prompt cree
  □ OutputSchema cree
  □ ExecutePromptAsync dans le service
```

### Validation
```
□ dotnet build OK
□ npm run build OK
□ npm run lint OK
□ Tests unitaires
□ Test manuel E2E
```

---

## SKILLS ASSOCIES

Ce skill orchestre les skills suivants :

| Skill | Phase | Action |
|-------|-------|--------|
| `/application` | 2-6 | Creation module full-stack |
| `/controller` | 5 | Generation controller API |
| `/notification` | 7.1 | Integration notifications |
| `/workflow` | 7.2 | Integration workflows/emails |
| `/ai-prompt` | 7.3 | Integration IA |
| `/ui-components` | 6 | Composants UI (EntityCard) |
| `/efcore:migration` | 4 | Migration EF Core |

---

## REGLES ABSOLUES

1. **TOUJOURS** suivre l'architecture en couches
2. **TOUJOURS** utiliser Factory Methods pour les entites
3. **TOUJOURS** implementer IAuditableEntity
4. **TOUJOURS** ajouter les permissions dans les 2 fichiers
5. **TOUJOURS** utiliser les hooks avec SignalR
6. **TOUJOURS** internationaliser (4 langues: fr, en, it, de)
7. **TOUJOURS** logger les operations importantes
8. **TOUJOURS** documenter l'API avec ProducesResponseType
9. **JAMAIS** d'acces DB direct depuis le frontend
10. **JAMAIS** de permissions hardcodees en strings
