- tasks/todo.md: sprint 4 plan with 9 user stories (US-17 → US-25), 9 décisions arrêtées - SPEC.md § Fonctionnement: Done is terminal, Reopen returns to review_required (open to all roles); engagement auto-flips planned → active when any simulation hits in_progress, no auto-rollback - SPEC.md § Référentiel MITRE: sprint 3 multi-tech + sprint 4 tactic_ids separated field - SPEC.md § UI/UX (new): theming light/dark/system with system default, button convention (icon + ≤8-char label), modal focus trap V1 - SPEC.md § Workflows: design-reviewer inserted between frontend-builder and code-reviewer; PR via make open-pr Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
22 KiB
Sprint 4 — UI polish + workflow tightening + dark mode + process hygiene
Branche : sprint/4-ui-polish
Statut : 🟡 DRAFT — 9 décisions arrêtées (3 nouvelles 2026-05-27 + 5 sprint 4 mémoire + 1 du PR helper), spec-reviewer en validation
Base : main @ 27573f5 (sprint 3 mergé via PR #6) + ba313a3 (carry-over SPEC sprint 3)
Objectif : absorber les 7 retours QA sprint 3 (UI/UX, workflow, alignement) + livrer le dark mode + durcir le process UI (design-reviewer agent + screenshots mandatory) + automatiser l'ouverture de PR. Pas de hotfix sprint 3 séparé — tout dans sprint 4 (décision user 2026-05-27).
0. SPEC.md updates
- ✅
ba313a3— § Simulation : "Type d'attaque MITRE correspondant (peut être une liste de référence)" → "Types d'attaque MITRE correspondants (multi-techniques) ..." (carry-over manquant de sprint 3 §0). - 🟡 § Fonctionnement à enrichir en début de sprint 4 :
- Préciser que "Done" est terminal : aucune édition possible sans Reopen explicite.
- Préciser que la transition Reopen
Done → Review requiredest ouverte à admin/redteam/soc. - Préciser que la création/avancement d'une simu fait avancer l'engagement de
plannedàactiveautomatiquement (jamais l'inverse).
- 🟡 § Décisions techniques à enrichir :
- Section "UI/UX" : convention boutons (icônes / symboles préférés aux longs libellés).
- Section "Theming" : dark mode supporté, toggle topbar, défaut =
prefers-color-schemedu système, persistancelocalStorage.
L'évolution est tracée dans CHANGELOG.md § Changed sprint 4.
1. User stories
US-17 — UI polish : dédoublonnage boutons + alignement + icônes
Pourquoi : QA sprint 3 — EngagementsListPage montre 2 boutons "New engagement" + "Create engagement" qui font la même chose ; le bouton Create de UsersAdminPage reste mal aligné malgré le fix sprint 2.
Critères d'acceptation
- AC-17.1 :
EngagementsListPagen'affiche qu'UN SEUL bouton "New engagement". Le doublon "Create engagement" est supprimé. - AC-17.2 : convention nouveaux boutons d'action (Create / Add / Save / Delete) : icône lucide-react ou unicode + label court (≤ 8 chars), pas de phrases. Audit des boutons existants : ne refactoriser que ceux qui dépassent ce seuil, garder les "Mark for review" / "Clear all" qui sont déjà courts ou ont une sémantique sans icône évidente. Boutons à passer en icône+label : "Save Red Team" → "Save" + icône, "Save SOC" → "Save SOC" + icône, "ADD TECHNIQUE" → "+" + "Add", "QUICK SEARCH" → "🔍" + "Search".
- AC-17.3 :
UsersAdminPageformulaire "Create account" — les 3 FormField (Username, Password, Role) ont leurs labels alignés sur la même baseline ET leurs inputs alignés sur la même baseline. Le bouton Create est aligné horizontalement avec la rangée des inputs. Pixel-perfect au niveau visuel à 1280×720.
US-18 — Simulation done = read-only + Reopen
Pourquoi : QA sprint 3 — actuellement une simu done peut toujours être PATCHée, ce qui contredit le statut terminal.
Critères d'acceptation
- AC-18.1 :
PATCH /api/simulations/<sid>avec status courantdoneretourne 409{error: "simulation is done — reopen first"}quel que soit le rôle. - AC-18.2 : nouvelle transition
POST /api/simulations/<sid>/transition {to: "review_required"}quand status courant ==done→ 200, autorisée admin + redteam + soc. Met à jourupdated_at. - AC-18.3 : la transition
→ review_requireddepuispending/in_progressgarde le comportement sprint 2 (admin/redteam only). La nouvelle règle s'ajoute SEULEMENT pour le casdone. - AC-18.4 : sur
SimulationFormPage, quand status ==done:- Tous les champs (RT + SOC) sont disabled.
MitreTechniquesFielden read-only (chips sans ×, input + icône matrice masqués).- L'action bar affiche UNIQUEMENT un bouton "Reopen" (visible admin/redteam/soc).
- Save RT, Save SOC, Mark for review, Close, Delete sont masqués.
- AC-18.5 : click Reopen → POST transition, toast
'Simulation reopened', badge se met à jour, les champs redeviennent éditables selon le rôle.
US-19 — Engagement auto-status planned → active
Pourquoi : QA sprint 3 — un engagement reste planned même quand ses simulations sont in_progress.
Critères d'acceptation
- AC-19.1 : quand une simulation transitionne vers
in_progress(auto-transition via PATCH RT-field non vide), si son engagement parent estplanned, l'engagement passe àactivedans la même unité de travail DB. - AC-19.2 : si l'engagement est déjà
activeouclosed, pas de changement. - AC-19.3 : aucun retour arrière auto. La transition
closedreste manuelle. - AC-19.4 : le frontend invalide
["engagement", eid]et["engagements"]après chaque PATCH/transition simulation pour récupérer le statut à jour.
US-20 — Matrice MITRE : look attack.mitre.org + pas de scroll horizontal
Pourquoi : QA sprint 3 — la matrice actuelle a un scroll horizontal et un layout maison.
Critères d'acceptation
- AC-20.1 :
MitreMatrixModalest élargi àmax-w-[98vw]. - AC-20.2 : layout 12 colonnes (12 tactiques Enterprise) qui tiennent SANS scroll horizontal à 1280×720 min. Largeur cellule technique ~95-110px (vs 220px actuel), font
text-[12px]. - AC-20.3 : couleurs cohérentes DESIGN.md ET visuellement proches de attack.mitre.org : header tactic avec fond contrasté + label uppercase tracking, techniques en cellules
bg-canvasavec hairline border, hoverbg-fog, sélectionnéebg-primarytexte blanc. - AC-20.4 : scroll vertical autorisé (
max-h-[80vh] overflow-y-auto). Jamais de scroll horizontal. - AC-20.5 : sub-techniques expand/collapse PRÉSERVÉ — pas de régression sprint 3 AC-15.2. Compteur "N selected" par tactique reste lisible.
- AC-20.6 : screenshot comparaison Mimic matrix vs attack.mitre.org joint au summary frontend-builder.
US-21 — Sélection de tactique en plus des techniques
Pourquoi : QA sprint 3 — l'utilisateur veut tagger une simulation par TACTIQUE (ex : TA0007 Discovery) sans devoir choisir une technique précise.
Critères d'acceptation
- AC-21.1 : modèle
Simulationgagne un champtactic_ids(colonne JSON, liste de strings TA-id, défaut[]). Séparé detechniques. - AC-21.2 : migration Alembic
0004_simulation_tactic_ids.py— ADD COLUMNtactic_ids(JSON, NOT NULL, default[]). Pas besoin de batch pour ADD COLUMN (SQLite natif). Aucun backfill (default suffit). - AC-21.3 : sérialisation Simulation expose
tactics: [{id, name}]enrichi à partir detactic_ids(id snapshot + name dérivé du bundle MITRE au runtime, comme pourtechniques). - AC-21.4 :
PATCH /api/simulations/<sid>accepte{tactic_ids: ["TA0007", ...]}. Validation : chaque ID doit exister (préfixeTA, présent dans_TACTIC_ORDER). Dedup serveur. ID inconnu → 400. Bundle non chargé → 503. - AC-21.5 :
tactic_idsest ajouté au gate SOC :(REDTEAM_FIELDS | {"technique_ids", "tactic_ids"}) & payload.keys(). SOC envoie → 403. Auto-transition se déclenche aussi sitactic_idsnon vide. - AC-21.6 :
MitreMatrixModal— le header de chaque colonne tactique devient cliquable (toggle de la tactique elle-même). État visuel distinct des techniques sélectionnées. Compteur passe àN+M selected(techniques + tactique). - AC-21.7 :
MitreTechniquesField— tactiques sélectionnées affichées comme chips distincts (style différencié :bg-primary text-canvasau lieu debg-primary-soft text-primary-deep). × pour retirer. Auto-save sur add/remove.
US-22 — Refonte input MITRE dans le form
Pourquoi : QA sprint 3 — pattern actuel (2 boutons textuels) trop verbeux.
Critères d'acceptation
- AC-22.1 : sous le label "MITRE Techniques", le composant affiche :
- Une rangée de chips (techniques + tactiques sélectionnées).
- En dessous, une rangée
[input texte autocomplete] [icône matrice]. - L'input fait l'autocomplete inline (debounce 200ms, dropdown ↑↓Enter, comme sprint 2 mais EMBARQUÉ).
- L'icône matrice à droite ouvre
MitreMatrixModal. - Aucun bouton textuel "Add Technique" ni "Quick Search".
- AC-22.2 : les chips affichent UNIQUEMENT la référence (T-id ou TA-id, ex :
T1059.001ouTA0007). Le nom apparaît au survol viatitle=attribute. - AC-22.3 :
MitreTechniquePickerexistant est intégré dans le nouveau layout comme l'autocomplete inline. Garde la signatureonSelect. - AC-22.4 : empty state : message court ("No techniques selected") dans la zone des chips. L'input et l'icône matrice restent visibles.
- AC-22.5 : mode read-only (SOC sur simu non-done, ou tous sur simu done) : chips sans ×, input + icône cachés.
US-23 — Dark mode
Pourquoi : ergonomie demandée. Sprint 4 framing acté.
Critères d'acceptation
- AC-23.1 : un toggle theme dans la topbar (
Layout.tsx), à droite du nom user. Icône lucide-reactSun/Moon/Monitor. - AC-23.2 : 3 états :
light,dark,system(auto = suitprefers-color-scheme). Toggle cycle entre les 3. - AC-23.3 : persistance via
localStorage(clémimic-theme, valeur'light'|'dark'|'system', défaut'system'). - AC-23.4 : Tailwind
darkMode: 'class'activé. Classedarkappliquée sur<html>selon le résolu. Tokens DESIGN.md étendus avec variantes dark (canvas, paper, ink, graphite, charcoal, etc.). Primary HP Electric Blue garde sa teinte. - AC-23.5 : tous les composants principaux audités et utilisent les classes Tailwind
dark:bg-.../dark:text-.... Pas de couleur hardcodée. - AC-23.6 : screenshots light + dark de
EngagementsListPage,SimulationFormPage,MitreMatrixModalouverte. Joints au summary.
US-24 — Process hygiene : design-reviewer agent + screenshots mandatory
Pourquoi : sprint 4 framing acté. Sprint 2/3 avait laissé passer des bugs visuels faute de pass design dédié.
Critères d'acceptation
- AC-24.1 : nouveau fichier
.claude/agents/design-reviewer.md. Brief : revoit le diff frontend + les screenshots fournis par le frontend-builder, audit alignement / hiérarchie typo / DESIGN.md token usage / responsive sanity / cohérence visuelle. Read-only. Lance après frontend-builder, avant code-reviewer. - AC-24.2 :
.claude/agents/frontend-builder.mdmis à jour pour rendre EXPLICITE que screenshots sont MANDATORY avant de marquer la tâche terminée (au moins 1 par feature visible / état modifié). Liste explicite des screenshots attendus dans le summary. - AC-24.3 : workflow sprint mis à jour dans SPEC.md § Workflows : ajouter design-reviewer entre frontend-builder et code-reviewer.
US-25 — Infra : PR helper script + Makefile target
Pourquoi : capitaliser le pattern Gitea API curl utilisé en sprint 3 pour automatiser les PRs.
Critères d'acceptation
- AC-25.1 :
scripts/open-pr.sh(executable,set -euo pipefail). Lit~/.git-credentials. Args :--sprint=N,--title="...",--body=path. Détecte la branche courante + owner/repo depuisgit remote get-url origin. POST/api/v1/repos/{owner}/{repo}/pulls. Imprime PR URL. - AC-25.2 : target Makefile
open-pr SPRINT=N TITLE="..." BODY=pathwrap le script. - AC-25.3 : documenté dans README.md (1 paragraphe).
- AC-25.4 : team-lead utilise ce target pour ouvrir la PR sprint 4 (dogfooding).
2. Brief technique — Backend Builder
Scope strict : backend/. Pas de touche au frontend, e2e, .claude/agents/, scripts/, Makefile, docs.
Livrables
Modèle Simulation — ajout uniquement :
tactic_ids: Mapped[list[str]] = mapped_column(JSON, nullable=False, default=list)
Migration Alembic 0004_simulation_tactic_ids.py :
- Upgrade :
op.add_column('simulations', sa.Column('tactic_ids', sa.JSON(), nullable=False, server_default=sa.text("'[]'"))). ADD COLUMN OK sans batch sur SQLite.server_defaultrègle le NOT NULL pour les lignes existantes. - Downgrade :
with op.batch_alter_table('simulations') as batch_op: batch_op.drop_column('tactic_ids'). - Test : schéma post-upgrade a
tactic_idsNOT NULL avec default[].
Serializer : serialize_simulation(sim) ajoute tactics: [{id, name}] enrichi runtime.
Service MITRE :
- Nouvelle fonction
lookup_tactic(tactic_id)→{id, name}ou None. - Nouvelle fonction
get_tactic_name(tactic_id)→ name string ou fallback id. _TACTIC_ORDER/_TACTIC_NAMESréutilisés.
Service workflow simulation_workflow.py — modifications :
- Guard
done(AC-18.1) : tout en haut deapply_patch, AVANT le check RBAC, sisimulation.status == "done"→ 409{error: "simulation is done — reopen first"}. - SOC gate étendu :
(REDTEAM_FIELDS | {"technique_ids", "tactic_ids"}) & payload.keys(). - Validation
tactic_idsupfront (similaire àtechnique_ids) : tous les IDs validés contre le bundle, dedupdict.fromkeys. Bundle non chargé → 503. - Auto-transition : ajouter le check
len(payload["tactic_ids"]) > 0au calculauto_trigger. - Transition
done → review_required(AC-18.2) : ajouter ce cas au state machine. Autorisé admin + redteam + soc. Updateupdated_at. Autres transitions depuisdone→ 409. - Hook engagement auto-status (AC-19.1) : après une transition de simu vers
in_progress(auto OU manual), appeler une fonction_maybe_activate_engagement(simulation)qui, sisimulation.engagement.status == "planned", setengagement.status = "active"et add à la session (commit en même temps que la simu).
API simulations.py :
- PATCH : le check status==done est fait dans
apply_patch(voir au-dessus). - Transition : accepter le nouveau cas done → review_required pour admin/redteam/soc.
Tests pytest
test_simulations_tactics.py(nouveau) : PATCH valide, ID inconnu → 400, bundle absent → 503, dedup, auto-transition, SOC → 403.test_simulations_done_readonly.py(nouveau) : PATCH simu done → 409 (admin/redteam/soc). Reopen via transition → 200. Autres transitions depuis done → 409. Après reopen, PATCH OK.test_engagement_lifecycle.py(nouveau) : création simu → engagement resteplanned. PATCH simu → simu in_progress + engagement active. Engagement déjà active → pas de changement. Engagement closed → pas de changement.- Migration test :
tactic_idscolumn NOT NULL après upgrade 0004 (similaire au pattern Alembic round-trip sprint 3). - Adapter
test_simulations_crud.py,test_simulations_patch.py,test_simulations_workflow.pysi nécessaire pour les assertions surtacticset la garde done.
Quality bar : ruff + mypy clean, tous les tests existants + nouveaux verts.
Règles
- Pas de touche au frontend,
.claude/agents/,scripts/,Makefile. - Renvoyer le summary attendu (cf
.claude/agents/backend-builder.md).
3. Brief technique — Frontend Builder
Scope strict : frontend/ UNIQUEMENT.
SCREENSHOTS MANDATORY (lesson sprint 2/3) : à la fin de ton travail, lance le dev server et fournis ≥ 5 screenshots :
EngagementsListPagelight + darkSimulationFormPageavec ≥ 2 chips technique + ≥ 1 chip tactique light + darkMitreMatrixModalouverte avec sélections light + darkUsersAdminPageform "Create account" (alignement vérifié) light + darkSimulationFormPagestatusdone(read-only + Reopen visible) light
Paths absolus dans le summary final. Si le dev server n'a pas pu tourner, dis-le EXPLICITEMENT avec les raisons techniques précises.
Livrables
US-17 — UI polish
EngagementsListPage.tsx: supprimer le doublon "Create engagement". Garder un seul CTA "New" + icône+(selon convention AC-17.2).UsersAdminPage.tsx: retravailler la grille pour pixel-perfect alignment. Choix laissé au builder (align-items: stretch + align-self, ou restructurer en 2 rangées).- Audit boutons : refactoriser ceux qui dépassent ≤ 8 chars. Garder "Mark for review" / "Clear all" / "Reopen" sans icône si pas d'icône évidente. Boutons à passer en icône+label : "Save Red Team" → icône + "Save", "Save SOC" → icône + "Save SOC", "ADD TECHNIQUE" → "+" + "Add" (rendu obsolète par US-22), "QUICK SEARCH" → "🔍" + "Search" (rendu obsolète par US-22).
US-18 — Done read-only + Reopen
SimulationFormPage.tsx:- Quand
simulation.status === 'done': tous champs disabled,MitreTechniquesField disabled, action bar montre UNIQUEMENT "Reopen" + icône (↻). - Bouton Reopen : visible admin/redteam/soc, click →
useTransitionSimulationtoreview_required, toast.
- Quand
US-19 — Engagement auto-status (côté UI)
useUpdateSimulationetuseTransitionSimulation: ajouter["engagement", eid]et["engagements"]aux invalidations après mutation réussie. Pas d'autre changement visuel.
US-20 — Matrice MITRE attack.mitre.org look
MitreMatrixModal.tsxoverhaul :max-w-[98vw],max-h-[80vh] overflow-y-auto, JAMAIS de scroll horizontal.display: grid; grid-template-columns: repeat(12, minmax(0, 1fr))pour répartir équitablement.- Cellule technique :
text-[12px], padding minimal, hairline border. - Header tactique : sticky top, fond contrasté, uppercase tracking, badge compteur à droite.
- Sub-techniques indent
pl-[8px], fondbg-cloud. - Search input top inchangé.
US-21 — Tactic selection
MitreMatrixModal.tsx: header de tactique cliquable (toggle). État visuel distinct.- Apply renvoie
{techniques, tactics}au parent. MitreTechniquesField.tsx: tactic chips style différenciébg-primary text-canvas. Auto-save tactic_ids.
US-22 — Refonte input MITRE
MitreTechniquesField.tsx:- Layout : chips area | input autocomplete inline + icône matrice button.
- Plus de boutons textuels "Add Technique" / "Quick Search".
- Chips compacts (T-id ou TA-id seul, name en
title=). - Empty state minimal.
US-23 — Dark mode
Layout.tsx: toggle theme dans la topbar. HookuseTheme()(localStorage + media query). 3 états avec cycle.tailwind.config.ts:darkMode: 'class'. Tokens étendus avec variantes dark (recommandé via CSS variables sous.dark { ... }dansindex.css, comme ça les composants n'ont pas à dupliquer leurs classes).- Audit tous les composants : aucune couleur hardcodée (pas de
bg-white,text-black,#xxxxxxinline). Tous passent un check visuel light + dark.
Règles
- Lit le summary backend EN PREMIER.
- Pas d'invention d'endpoints.
- Réutiliser les patterns sprint 1/2/3.
- Respect DESIGN.md tokens.
- Pas de dépendance npm sans escalade (sauf
lucide-reactautorisé). - Interdiction absolue de toucher
e2e/,backend/,.claude/agents/,scripts/,Makefile.
4. Brief — Team-lead infra (US-24 + US-25, en parallèle des builders)
US-24 — Process hygiene
- Créer
.claude/agents/design-reviewer.mdavec frontmatter agent (modelopus, tools :Read,Glob,Grep,Bashlecture seule). Brief : revoit diff frontend + screenshots, audit alignement / DESIGN.md tokens / cohérence visuelle / responsive. - Mettre à jour
.claude/agents/frontend-builder.md: DoD strict sur les screenshots. - Mettre à jour SPEC.md § Workflows : insérer design-reviewer entre frontend-builder et code-reviewer.
US-25 — PR helper
- Écrire
scripts/open-pr.sh(cf AC-25.1). - Target Makefile
open-pr. - Documenter README.md.
- Dogfood en fin de sprint.
5. Brief — Test verifier
E2e Playwright :
us17-ui-polish.spec.ts— AC-17.1 (single button), AC-17.3 (alignment via locator boundingBox).us18-done-readonly-reopen.spec.ts— AC-18.1 → AC-18.5.us19-engagement-auto-status.spec.ts— AC-19.1 → AC-19.4.us20-matrix-fits-modal.spec.ts— AC-20.1, AC-20.4 (no horizontal scroll viaboundingBox).us21-tactic-selection.spec.ts— AC-21.4 → AC-21.7.us22-mitre-input-redesign.spec.ts— AC-22.1 → AC-22.5.us23-dark-mode.spec.ts— AC-23.1 → AC-23.3.
US-24/25 non e2e (process / repo files). Couverture par dogfood (la PR sprint 4 elle-même est ouverte via make open-pr).
Adapter les sprint 2/3 e2e si l'audit boutons (AC-17.2) renomme certains labels.
6. Décisions arrêtées
- Tactic storage : colonne JSON
tactic_idsséparée. ✓ 2026-05-27 - Dark mode default :
system(suitprefers-color-scheme, fallbacklightsi non détecté). ✓ 2026-05-27 - Matrix CSS fidelity : look similaire qualitatif (frontend-builder itère, pas pixel-perfect). ✓ 2026-05-27
- Reopen target :
done → review_required. ✓ mémoire (sprint 4 scope) - Reopen RBAC : admin + redteam + soc. ✓ mémoire
- Engagement auto trigger :
planned → activesur 1ère simu in_progress (auto-transition ou manual). Pas de retour arrière auto. ✓ mémoire - PR helper token source :
~/.git-credentials(parse user + token via sed, cf reference-gitea-pr-api). ✓ 2026-05-27 - Workflow design-reviewer : insérée entre frontend-builder et code-reviewer, read-only. Diff frontend + screenshots. Format rapport à la code-reviewer mais focus visuel/design. ✓ mémoire
- Screenshots frontend-builder : MANDATORY au sprint 4, en sortie du frontend-builder, paths absolus dans summary, refus de marquer la tâche done sans. ✓ mémoire
7. Plan d'exécution
- ✅ Team-lead a re-appliqué le SPEC sprint 3 oublié (
ba313a3). - ✅ User a validé les 4 décisions ouvertes (tactic separated, theme system, matrix qualitative, token from ~/.git-credentials). Avec les 5 acquises en mémoire (sprint 4 scope), ça fait 9 décisions arrêtées.
- 🟡 Team-lead met à jour SPEC.md § Workflows + § Décisions techniques (§0).
- 🟡 Spec-reviewer valide le plan vs SPEC.md (anti-trous comme à sprint 3 — RBAC field-level, batch SQLite, scope ambigu).
- 🔵 Backend-builder : modèle + migration 0004 + workflow done-readonly/reopen + engagement auto-lifecycle + tactic_ids + tests.
- 🔵 Frontend-builder : UI polish + done read-only + matrix overhaul + tactic selection + input redesign + dark mode + screenshots.
- 🔵 Team-lead (US-24 + US-25 en parallèle de frontend) : design-reviewer agent + frontend-builder.md update + scripts/open-pr.sh + Makefile target.
- 🔵 Design-reviewer (NEW STEP) : revoit diff frontend + screenshots.
- 🔵 Code-reviewer : revoit le diff complet (LSP-first).
- 🔵 Test-verifier : e2e US-17 → US-23.
- 🟢 Team-lead : PR via
make open-pr(dogfood AC-25.4) + récap.