feat: sprint 5 — simulation templates + instantiation + nav + dropdown #8

Merged
knacky merged 11 commits from sprint/5-templates into main 2026-06-07 16:08:39 +00:00

11 Commits

Author SHA1 Message Date
Knacky
e18ec2bf79 docs(lessons): sprint 5 — fold in the recurrent SPEC-uncommitted lesson with concrete fix candidates 2026-05-28 07:25:09 +02:00
Knacky
cbc176ab82 docs(spec): carry over sprint 5 SPEC update missed in sprint 5 commits
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>
2026-05-28 07:24:43 +02:00
Knacky
54959c7d5b test(e2e): sprint 5 acceptance — US-26 / US-27 / US-28 + adaptations dropdown sprint 2-4
- us26: add AC-26.4 isinstance guard (technique_ids string→400) + AC-26.7 cascade test (DELETE template does not affect instantiated sim)
- us27: add NIT-1 dropdown Escape/click-outside close, NIT-2 empty-engagement dropdown visibility
- 49 sprint 5 tests passing, 206/207 full suite passing (us1 pre-existing isolation issue)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 07:23:33 +02:00
Knacky
2e59743af5 docs: sprint 5 wrap-up — CHANGELOG + README + 6 lessons + plan final
- 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>
2026-05-28 07:18:21 +02:00
Knacky
7c011db6d9 test(e2e): sprint 5 acceptance tests — US-26 → US-28 + dropdown adaptations
Add three new spec files:
- us26-templates-crud: API CRUD (AC-26.3–26.7) + UI list/form/delete/redirect (AC-26.8)
- us27-instantiate-from-template: template_id copy + name override + 404 + decoupling
  (AC-27.1–27.3) + no auto-transition/engagement-activate (AC-27.4–27.5) + dropdown
  UI + picker modal + empty state + SOC gate (AC-27.6–27.7)
- us28-templates-nav: Templates link admin+redteam only, SOC redirect, form editable (AC-28.1–28.3)

Adapt sprint 2/3 e2e for sprint 5 dropdown:
- us4-engagements: getByRole link "New simulation" → getByTestId "new-simulation-btn"
- us7-simulation-create: same — split-button dropdown replaced the link

Suite: 201 passed (1 pre-existing flaky in us3 re DB state, unrelated to sprint 5).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 07:15:04 +02:00
Knacky
55f993fa24 fix(backend): sprint 5 post-review — name fallback, isinstance guards, 400 tests
- 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>
2026-05-28 07:04:25 +02:00
Knacky
33a0ca30bb fix(frontend): sprint 5 post-code-review — dropdown close-on-outside + empty-state dropdown
- 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>
2026-05-28 07:02:34 +02:00
Knacky
20783118ee fix(frontend): sprint 5 design-review — dropdown dark + Plus icon + re-shoots
- shadow-floating dark:shadow-floating-dark on dropdown menu (Fix 1)
- hover:bg-cloud dark:hover:bg-fog on dropdown items (Fix 2)
- Plus icon + "New" label on split-button primary half (Fix 3)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 06:50:19 +02:00
Knacky
2b700115e8 fix(frontend): sprint 5 — correct API path /simulation-templates → /templates
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 06:43:33 +02:00
Knacky
90fc5bab6c feat(frontend): sprint 5 — templates CRUD pages + nav + picker modal + dropdown
- types.ts: SimulationTemplate, SimulationTemplateCreateInput, SimulationTemplatePatchInput,
  extend SimulationCreateInput with template_id
- api/templates.ts: listTemplates, getTemplate, createTemplate, updateTemplate, deleteTemplate
- hooks/useTemplates.ts: useTemplates, useTemplate, useCreateTemplate, useUpdateTemplate,
  useDeleteTemplate (TanStack Query, invalidates ["templates"])
- TemplatesListPage: /admin/templates — table (name, MITRE count, created by, updated),
  New/Edit/Delete actions, loading/error/empty states
- TemplateFormPage: /admin/templates/new + /admin/templates/:id/edit — controlled form
  with inline MITRE field (picker + matrix modal), ConfirmDialog for delete
- TemplatePickerModal: reusable modal listing templates with empty state (AC-27.6)
- SimulationList: replace "New simulation" link with split-button dropdown
  (Blank → /simulations/new | From template… → TemplatePickerModal + POST template_id)
- Layout: "Templates" nav link (admin | redteam, before "Users")
- App.tsx: /admin/templates routes gated roles=["admin","redteam"]
- 26 new Vitest tests (118 total, 92 original preserved)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 06:36:10 +02:00
Knacky
1f327e9aa8 feat(backend): sprint 5 — SimulationTemplate CRUD + instantiation
- SimulationTemplate model + migration 0005 (CREATE TABLE + name index)
- 5 CRUD endpoints under /api/templates (admin|redteam only, SOC 403)
- POST /api/engagements/<eid>/simulations extended with optional template_id
- serialize_template() reusing _enrich_techniques/_enrich_tactics helpers
- IntegrityError → 409 for duplicate name on both POST and PATCH
- 28 new tests (CRUD, RBAC, dedup, instantiation, migration round-trip)
- 221 tests pass; ruff clean; mypy clean

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 06:25:19 +02:00