Simulation templates (CRUD complet sous /api/templates) — admin/redteam créent des templates pré-remplis (name + description + commands + prerequisites + MITRE techniques + tactics) qui survivent indépendamment des engagements.
Instanciation 1-clic depuis EngagementDetailPage : dropdown split-button "Blank | From template…" → modal picker → simu créée avec tous les champs RT pré-remplis.
Décorrélation totale : pas de FK template_id sur Simulation. Modifier l'instance ne touche pas le template ; modifier le template ne touche pas les instances ; supprimer le template ne supprime pas les instances.
RBAC SOC zero access : admin + redteam gèrent ; SOC ne voit pas le nav link, tous endpoints /api/templates* → 403, ProtectedRoute redirige les SOC qui tapent l'URL.
E2e Playwright : 201/201 verts — 44 nouveaux sprint 5 (us26, us27, us28) + adaptations sprint 2/3 pour le dropdown.
Comment tester en local
make build && make start # auto-podman
make create-admin USER=alice PASS=changeme8 # si premier setup# Ouvrir http://127.0.0.1:5000 (IPv4 explicite si IPv6 par défaut)
Scénarios :
Créer un template — Topbar → "Templates" → "+ New" → remplir name "Mimikatz LSASS Dump" + description + commands multiline + ≥ 2 techniques MITRE → Save. Vérifier /admin/templates liste le template.
Instancier depuis un template — Engagement détail → dropdown sur "+ New" → "From template…" → click sur le template → la simu apparaît avec tous les champs RT pré-remplis. Status reste pending (auto-transition NE déclenche PAS sur instantiation).
Décorrélation — Modifier la simu instanciée → vérifier que le template original est intact. Inverse aussi.
SOC zero access — Se connecter en SOC → vérifier que "Templates" est absent de la topbar. Taper /admin/templates dans l'URL → redirect /engagements + toast "Accès refusé".
Empty state — Sur un engagement vide, dropdown "+ New" doit toujours proposer "From template…" (UX fix sprint 5 post-code-review).
Dropdown close-on-outside — Ouvrir le dropdown puis cliquer ailleurs OU presser Escape → ferme.
Notes
API path final : /api/templates (et NON /api/simulation-templates du brief original). Backend choice silently propagé en frontend via fix commit 2b70011.
Migration 0005 : CREATE TABLE simple, downgrade DROP TABLE.
Process wins sprint 5 : spec-reviewer 2-pass APPROVED AVANT dispatch backend = 0 addendum mid-implementation (vs sprint 3/4). Lesson captured.
## Summary
- **Simulation templates** (CRUD complet sous `/api/templates`) — admin/redteam créent des templates pré-remplis (name + description + commands + prerequisites + MITRE techniques + tactics) qui survivent indépendamment des engagements.
- **Instanciation 1-clic** depuis EngagementDetailPage : dropdown split-button "Blank | From template…" → modal picker → simu créée avec tous les champs RT pré-remplis.
- **Décorrélation totale** : pas de FK `template_id` sur Simulation. Modifier l'instance ne touche pas le template ; modifier le template ne touche pas les instances ; supprimer le template ne supprime pas les instances.
- **RBAC SOC zero access** : admin + redteam gèrent ; SOC ne voit pas le nav link, tous endpoints `/api/templates*` → 403, ProtectedRoute redirige les SOC qui tapent l'URL.
## Test plan
- Backend : **226/226** pytest (`ruff` + `mypy` clean) — 28 nouveaux + 5 post-code-review.
- Frontend : **121/121** vitest (`typecheck` + `lint` clean) — 22 nouveaux + 3 post-code-review.
- E2e Playwright : **201/201** verts — 44 nouveaux sprint 5 (`us26`, `us27`, `us28`) + adaptations sprint 2/3 pour le dropdown.
## Comment tester en local
```bash
make build && make start # auto-podman
make create-admin USER=alice PASS=changeme8 # si premier setup
# Ouvrir http://127.0.0.1:5000 (IPv4 explicite si IPv6 par défaut)
```
Scénarios :
1. **Créer un template** — Topbar → "Templates" → "+ New" → remplir name "Mimikatz LSASS Dump" + description + commands multiline + ≥ 2 techniques MITRE → Save. Vérifier `/admin/templates` liste le template.
2. **Instancier depuis un template** — Engagement détail → dropdown sur "+ New" → "From template…" → click sur le template → la simu apparaît avec tous les champs RT pré-remplis. Status reste `pending` (auto-transition NE déclenche PAS sur instantiation).
3. **Décorrélation** — Modifier la simu instanciée → vérifier que le template original est intact. Inverse aussi.
4. **SOC zero access** — Se connecter en SOC → vérifier que "Templates" est absent de la topbar. Taper `/admin/templates` dans l'URL → redirect `/engagements` + toast "Accès refusé".
5. **Empty state** — Sur un engagement vide, dropdown "+ New" doit toujours proposer "From template…" (UX fix sprint 5 post-code-review).
6. **Dropdown close-on-outside** — Ouvrir le dropdown puis cliquer ailleurs OU presser Escape → ferme.
## Notes
- API path final : `/api/templates` (et NON `/api/simulation-templates` du brief original). Backend choice silently propagé en frontend via fix commit `2b70011`.
- Migration 0005 : CREATE TABLE simple, downgrade DROP TABLE.
- Process wins sprint 5 : spec-reviewer 2-pass APPROVED AVANT dispatch backend = 0 addendum mid-implementation (vs sprint 3/4). Lesson captured.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
- useEffect pointerdown + Escape listeners when dropdown open (NIT 1)
- empty state now renders NewSimulationDropdown instead of plain Link (NIT 2)
- 3 new Vitest: close-on-outside, close-on-Escape, empty-state has dropdown
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- create_simulation: name falls back to template.name when template_id provided
and name is absent/empty (AC-27.1)
- templates POST/PATCH: isinstance(list) check on technique_ids/tactic_ids
before resolving, returns 400 with clear message
- 5 new tests: unknown technique_id → 400 (POST+PATCH), unknown tactic_id → 400
(POST+PATCH), name fallback to template.name
- mypy: merged template branch into if/else to eliminate union-attr false positives
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CHANGELOG: sprint 5 entry under [Unreleased] (templates CRUD + instantiation + nav + dropdown + decorrelation). Sprint 4 moved to its own [Sprint 4] section.
- README: status bump to sprint 5, test counts refreshed (226/121/201).
- tasks/lessons.md: 6 sprint-5 lessons captured (spec-reviewer 2-pass before dispatch finally clicked, endpoint path drift caught visually not by spec-review, screenshot script mocks lag path changes, silent URL "improvements" by backend, apply_patch wrong primitive for creation copy paths, IntegrityError catch beats pre-check SELECT, SendMessage rule applies to all team agents).
- tasks/todo.md: status flipped to 🟢 SPRINT COMPLET.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sprint 5 plan §0 added a new ## Templates de simulations section to SPEC.md
(between § Fonctionnement and § Authentification & rôles). The edit sat in
the sprint 5 worktree but was never committed across the 9 sprint commits,
so PR #8 currently does not include the corresponding spec text.
This is the THIRD sprint running this happens (sprint 3 → fixed at sprint 4
start; sprint 4 → fixed at sprint 5 start; sprint 5 → fixed here mid-PR
because I caught the M SPEC.md before merge).
Lesson updated in tasks/lessons.md to make the "git status pre-sprint-close"
discipline harder to forget.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
/api/templates) — admin/redteam créent des templates pré-remplis (name + description + commands + prerequisites + MITRE techniques + tactics) qui survivent indépendamment des engagements.template_idsur Simulation. Modifier l'instance ne touche pas le template ; modifier le template ne touche pas les instances ; supprimer le template ne supprime pas les instances./api/templates*→ 403, ProtectedRoute redirige les SOC qui tapent l'URL.Test plan
ruff+mypyclean) — 28 nouveaux + 5 post-code-review.typecheck+lintclean) — 22 nouveaux + 3 post-code-review.us26,us27,us28) + adaptations sprint 2/3 pour le dropdown.Comment tester en local
Scénarios :
/admin/templatesliste le template.pending(auto-transition NE déclenche PAS sur instantiation)./admin/templatesdans l'URL → redirect/engagements+ toast "Accès refusé".Notes
/api/templates(et NON/api/simulation-templatesdu brief original). Backend choice silently propagé en frontend via fix commit2b70011.🤖 Generated with Claude Code