Files
mimic/tasks/todo.md
Knacky 6d2bb091e2 docs: sprint 4 wrap-up — CHANGELOG + README + 7 lessons + plan final
- CHANGELOG: sprint 4 entry under [Unreleased] (covers all 9 US: dark mode, MITRE matrix overhaul, tactic_ids, done read-only + Reopen, engagement auto-status, UI polish, design-reviewer agent, PR helper, screenshots mandatory). Sprint 3 moved to its own [Sprint 3] section.
- README: status bump, test counts refreshed (193/92/158).
- tasks/lessons.md: 7 sprint-4 lessons captured (git status before sprint close, endpoint round-trip mismatch caught only by e2e, ink vs slab token split, structural row layout > class tweaks, hardcoded paths in migration tests, screenshots with auth, builder cross-context summaries as accidental re-dispatch).
- tasks/todo.md: status flipped to 🟢 SPRINT COMPLET, execution sequence ticks updated with commit hashes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 21:41:47 +02:00

27 KiB
Raw Blame History

Sprint 4 — UI polish + workflow tightening + dark mode + process hygiene

Branche : sprint/4-ui-polish Statut : 🟢 SPRINT COMPLET — backend 193/193 + frontend 92/92 + e2e 158/158, PR prête 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 required est ouverte à admin/redteam/soc.
    • Préciser que la création/avancement d'une simu fait avancer l'engagement de planned à active automatiquement (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-scheme du système, persistance localStorage.

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 : EngagementsListPage n'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 : UsersAdminPage formulaire "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 courant done retourne 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 à jour updated_at.
  • AC-18.3 : la transition → review_required depuis pending/in_progress garde le comportement sprint 2 (admin/redteam only). La nouvelle règle s'ajoute SEULEMENT pour le cas done.
  • AC-18.4 : sur SimulationFormPage, quand status == done :
    • Tous les champs (RT + SOC) sont disabled.
    • MitreTechniquesField en 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 est planned, l'engagement passe à active dans la même unité de travail DB.
  • AC-19.2 : si l'engagement est déjà active ou closed, pas de changement.
  • AC-19.3 : aucun retour arrière auto. La transition closed reste 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 : MitreMatrixModal est é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-canvas avec hairline border, hover bg-fog, sélectionnée bg-primary texte 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 Simulation gagne un champ tactic_ids (colonne JSON, liste de strings TA-id, défaut []). Séparé de techniques.
  • AC-21.2 : migration Alembic 0004_simulation_tactic_ids.py — ADD COLUMN tactic_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 de tactic_ids (id snapshot + name dérivé du bundle MITRE au runtime, comme pour techniques).
  • AC-21.4 : PATCH /api/simulations/<sid> accepte {tactic_ids: ["TA0007", ...]}. Validation : chaque ID doit exister dans _TACTIC_IDS (mapping TA-id → short-name, cf §2 Service MITRE). Dedup serveur. ID inconnu → 400. Pas de check mitre_loaded : les TA-ids sont une constante MITRE standard stable hardcodée dans _TACTIC_IDS — la validation ne dépend pas du bundle STIX runtime (contrairement aux technique_ids qui requièrent le bundle). Donc PATCH tactic_ids reste OK même si le bundle est absent (alors que technique_ids retourne 503). Spec-aligné avec l'implémentation et les tests post-code-review.
  • AC-21.5 : tactic_ids est ajouté au gate SOC : (REDTEAM_FIELDS | {"technique_ids", "tactic_ids"}) & payload.keys(). SOC envoie → 403. Auto-transition se déclenche aussi si tactic_ids non 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-canvas au lieu de bg-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.001 ou TA0007). Le nom apparaît au survol via title= attribute.
  • AC-22.3 : MitreTechniquePicker existant est intégré dans le nouveau layout comme l'autocomplete inline. Garde la signature onSelect.
  • 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-react Sun / Moon / Monitor.
  • AC-23.2 : 3 états : light, dark, system (auto = suit prefers-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é. Classe dark appliqué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, MitreMatrixModal ouverte. 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.md mis à 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 depuis git 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=path wrap 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_default rè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_ids NOT NULL avec default [].

Serializer : serialize_simulation(sim) ajoute tactics: [{id, name}] enrichi runtime.

Service MITRE :

  • Sprint 3 a indexé les tactiques par short-name ("initial-access", "execution", ...) dans _TACTIC_ORDER et TACTIC_NAMES. La SPEC et le plan sprint 4 utilisent la notation TA-id ("TA0001", "TA0007", etc.). Il faut un mapping TA-id → short-name pour valider/résoudre les tactic_ids reçus.
  • Ajouter une constante module-level (12 entrées hardcodées, MITRE standard stable — attention, les TA-ids ne sont PAS séquentiels) :
    _TACTIC_IDS: dict[str, str] = {
        "TA0001": "initial-access",
        "TA0002": "execution",
        "TA0003": "persistence",
        "TA0004": "privilege-escalation",
        "TA0005": "defense-evasion",
        "TA0006": "credential-access",
        "TA0007": "discovery",
        "TA0008": "lateral-movement",
        "TA0009": "collection",
        "TA0011": "command-and-control",
        "TA0010": "exfiltration",
        "TA0040": "impact",
    }
    
  • Nouvelle fonction lookup_tactic(tactic_id: str) -> dict | None :
    short = _TACTIC_IDS.get(tactic_id)
    if short is None:
        return None
    return {"id": tactic_id, "name": TACTIC_NAMES[short]}
    
  • Nouvelle fonction get_tactic_name(tactic_id: str) -> str | None : pareil mais retourne juste le name.
  • Validation tactic_ids dans simulation_workflow.py : un id absent de _TACTIC_IDS → 400 {"error": "unknown tactic id: <id>"}.

Service workflow simulation_workflow.py — modifications :

  1. Guard done (AC-18.1) : tout en haut de apply_patch, AVANT le check RBAC, si simulation.status == "done" → 409 {error: "simulation is done — reopen first"}. Vaut pour TOUS les rôles, admin compris.
  2. SOC gate étendu : (REDTEAM_FIELDS | {"technique_ids", "tactic_ids"}) & payload.keys().
  3. Validation tactic_ids upfront (similaire à technique_ids) : tous les IDs validés contre le bundle, dedup dict.fromkeys. Bundle non chargé → 503.
  4. Auto-transition : ajouter le check len(payload["tactic_ids"]) > 0 au calcul auto_trigger.
  5. Transition done → review_required (AC-18.2)implémentation précise : le dict _ALLOWED_TRANSITIONS actuel est keyé par target status et a déjà une entrée "review_required" avec from={pending, in_progress} et roles={admin, redteam}. On NE peut PAS ajouter une 2e entrée avec la même clé. À la place, dans transition(), AVANT le lookup dict, ajoute un cas spécial qui suit les patterns existants du fichier :
    # transition() returns tuple[Any, int] | None — None on success, error tuple otherwise.
    # Existing functions use datetime.now(UTC) (timezone-aware, not deprecated utcnow).
    # Enum values are UPPERCASE: SimulationStatus.DONE, SimulationStatus.REVIEW_REQUIRED.
    if to_status == "review_required" and simulation.status == SimulationStatus.DONE:
        simulation.status = SimulationStatus.REVIEW_REQUIRED
        simulation.updated_at = datetime.now(UTC)
        db.session.commit()
        return None
    # ... reste de la fonction inchangée (dict lookup pour les autres cas)
    
    Pas de check explicite du rôle ici — @login_required upstream + l'enum User limité à admin/redteam/soc rendent la défense superflue (KISS). Autres transitions depuis done (vers pending, in_progress, done lui-même) → 409 via le dict lookup qui ne les couvre pas.
  6. 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, si simulation.engagement.status == "planned", set engagement.status = "active" et db.session.add(engagement). NE PAS appeler db.session.commit() dans le helper — le caller (api/simulations.py:update_simulation) gère le commit final, sinon double-commit.

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 reste planned. PATCH simu → simu in_progress + engagement active. Engagement déjà active → pas de changement. Engagement closed → pas de changement.
  • Migration test : tactic_ids column 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.py si nécessaire pour les assertions sur tactics et 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 :

  1. EngagementsListPage light + dark
  2. SimulationFormPage avec ≥ 2 chips technique + ≥ 1 chip tactique light + dark
  3. MitreMatrixModal ouverte avec sélections light + dark
  4. UsersAdminPage form "Create account" (alignement vérifié) light + dark
  5. SimulationFormPage status done (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 → useTransitionSimulation to review_required, toast.

US-19 — Engagement auto-status (côté UI)

  • useUpdateSimulation et useTransitionSimulation : ajouter ["engagement", eid] et ["engagements"] aux invalidations après mutation réussie. Pas d'autre changement visuel.
  • Note (spec-reviewer Pass 3) : eid n'est pas directement disponible dans la signature des hooks (qui prennent sid). Solution : lire engagement_id depuis la response simulation (le backend l'expose toujours, cf serialize_simulation sprint 2) OU le passer en arg supplémentaire au hook si plus propre. Pas un trou plan, juste à anticiper.

US-20 — Matrice MITRE attack.mitre.org look

  • MitreMatrixModal.tsx overhaul :
    • 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], fond bg-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.
  • PATCH combiné (spec-reviewer fix #4) : Apply depuis la matrice → UN SEUL PATCH {technique_ids: [...], tactic_ids: [...]} (les 2 listes ensemble). Pas 2 PATCH séquentiels (risque de race + risque que le 2nd appel hit le guard done). Pour les × remove ET les Quick Search adds, l'implémentation finale envoie aussi les 2 listes ensemble (save({techniques, tactics})) — fonctionnellement équivalent à un PATCH dimensionnel et plus simple à raisonner (single source of truth = local state). Spec-aligné post-code-review : "always send both dimensions" est la règle, le brief initial "dimension qui change" était over-spec. Toutes les mutations passent par useUpdateSimulation en un appel atomique.

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.
  • SimulationFormPage.tsx — call site update (spec-reviewer fix #4) : la signature de MitreTechniquesField change de value: MitreTechnique[] (sprint 3) à value: {techniques: MitreTechnique[], tactics: MitreTactic[]}. La page doit passer value={{techniques: sim.techniques, tactics: sim.tactics}} (le champ sim.tactics vient du nouveau serializer backend). TypeScript catch le miss mais flag-le explicitement pour ne pas l'oublier.

US-23 — Dark mode

  • Layout.tsx : toggle theme dans la topbar. Hook useTheme() (localStorage + media query). 3 états avec cycle.
  • tailwind.config.ts : darkMode: 'class'. Tokens étendus avec variantes dark (recommandé via CSS variables sous .dark { ... } dans index.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, #xxxxxx inline). 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-react autorisé).
  • 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.md avec frontmatter agent (model opus, tools : Read, Glob, Grep, Bash lecture 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 via boundingBox).
  • 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.

Spec-reviewer INFO B : AC-22.2 change le format des chips de "T1059 — Command and Scripting Interpreter" (sprint 3) à juste "T1059" (avec name dans title=). Les e2e sprint 3 (notamment us14-techniques-tags.spec.ts) qui assertent le format complet doivent être mis à jour. Pas seulement les labels boutons.


6. Décisions arrêtées

  1. Tactic storage : colonne JSON tactic_ids séparée. ✓ 2026-05-27
  2. Dark mode default : system (suit prefers-color-scheme, fallback light si non détecté). ✓ 2026-05-27
  3. Matrix CSS fidelity : look similaire qualitatif (frontend-builder itère, pas pixel-perfect). ✓ 2026-05-27
  4. Reopen target : done → review_required. ✓ mémoire (sprint 4 scope)
  5. Reopen RBAC : admin + redteam + soc. ✓ mémoire
  6. Engagement auto trigger : planned → active sur 1ère simu in_progress (auto-transition ou manual). Pas de retour arrière auto. ✓ mémoire
  7. PR helper token source : ~/.git-credentials (parse user + token via sed, cf reference-gitea-pr-api). ✓ 2026-05-27
  8. 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
  9. 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

  1. Team-lead a re-appliqué le SPEC sprint 3 oublié (ba313a3).
  2. 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.
  3. 🟡 Team-lead met à jour SPEC.md § Workflows + § Décisions techniques (§0).
  4. 🟡 Spec-reviewer valide le plan vs SPEC.md (anti-trous comme à sprint 3 — RBAC field-level, batch SQLite, scope ambigu).
  5. 🔵 Backend-builder : modèle + migration 0004 + workflow done-readonly/reopen + engagement auto-lifecycle + tactic_ids + tests.
  6. 🔵 Frontend-builder : UI polish + done read-only + matrix overhaul + tactic selection + input redesign + dark mode + screenshots.
  7. 🔵 Team-lead (US-24 + US-25 en parallèle de frontend) : design-reviewer agent + frontend-builder.md update + scripts/open-pr.sh + Makefile target.
  8. 🔵 Design-reviewer (NEW STEP) : revoit diff frontend + screenshots.
  9. 🔵 Code-reviewer : revoit le diff complet (LSP-first).
  10. 🔵 Test-verifier : e2e US-17 → US-23.
  11. 🟢 Team-lead : PR via make open-pr (dogfood AC-25.4) + récap.