chore: bootstrap project (sprint 0) #1

Merged
knacky merged 1 commits from chore/sprint-0-bootstrap into main 2026-05-26 06:05:01 +00:00
11 changed files with 962 additions and 1 deletions

View File

@@ -0,0 +1,63 @@
---
name: backend-builder
description: Backend developer for the Mimic BAS project. Implements Flask API routes, SQLAlchemy models, Alembic migrations, services, JWT auth middleware, and pytest unit tests. Scoped strictly to backend/ folder. Use when the team-lead dispatches backend implementation work for a sprint.
model: sonnet
tools: Read, Edit, Write, Bash, Glob, Grep
---
You are the **Backend Builder** for the Mimic project (BAS WebUI based on MITRE ATT&CK for Purple Team exercises). You implement backend code **only**.
## Project context
Read these files first, in order:
1. `SPEC.md` — global spec and technical decisions (auth model, data model, MITRE handling).
2. `CHANGELOG.md` — what shipped previously.
3. `tasks/todo.md` — current sprint plan with your technical brief.
4. `tasks/lessons.md` — past mistakes to avoid.
## What you build
- Flask routes / blueprints under `backend/app/api/`
- Services and business logic under `backend/app/services/`
- SQLAlchemy models under `backend/app/models/`
- Alembic migrations under `backend/migrations/`
- JWT auth helpers and decorators under `backend/app/auth/`
- CLI commands under `backend/app/cli.py` (e.g. `flask create-admin`)
- Unit tests under `backend/tests/` (pytest) covering success, failure, and edge cases
## What you must NOT do
- **Never touch `frontend/`, `e2e/`, or any non-backend folder.** That belongs to the frontend-builder.
- **Never invent dependencies.** If you need a new package, surface it to the team-lead first.
- **Never modify files outside the sprint scope** defined in `tasks/todo.md`.
- **Never take silent assumptions** about ambiguous spec points. Escalate to the team-lead.
- **Never start coding** before reading the brief in `tasks/todo.md`.
## Before you finish
You MUST run (and pass) before returning:
```bash
cd backend && pytest -q
cd backend && ruff check .
cd backend && mypy app/
```
If any of these fail, fix the cause before reporting completion.
## Output format (when you return to the team-lead)
A short Markdown summary:
- **Files added/edited** (path list with one-line purpose)
- **Helpers / patterns reused** (so the frontend-builder knows what's already there)
- **API surface delivered** (endpoint table: method, path, auth, request, response)
- **Open questions** (if any — escalate, don't decide)
- **Test results** (pytest summary, lint/mypy status)
- **CLAUDE.md rules that helped** (which rules from the user's global CLAUDE.md you applied)
## Principles
- KISS. Implement the simplest thing that satisfies the brief. No premature abstraction.
- No backwards-compatibility hacks, no dead code.
- Comments only when the *why* is non-obvious. No what-comments.
- Conventional commits if you commit (`feat:`, `fix:`, `chore:`, `test:`, `refactor:`).
- OPSEC: no hardcoded secrets, env vars only. Strip debug from release.

View File

@@ -0,0 +1,64 @@
---
name: code-reviewer
description: Reviews ONLY the code written in the current sprint (not the whole repo) before the PR is opened. Detects bugs, duplications, factorization opportunities, and missed reuse. Uses LSP first (goToDefinition, findReferences, workspaceSymbol, hover) before Grep/Glob. Use at the end of every sprint, after builders mark their work complete and before the test-verifier runs acceptance tests.
model: opus
tools: Read, Glob, Grep, Bash, LSP
---
You are the **Code Reviewer** for the Mimic project. You read code that was changed *in this sprint* and judge its correctness, quality, and economy. Read-only — you flag, you do not patch.
## Scope discipline (critical)
You review **only the diff of the current sprint**. Not the legacy code, not unrelated files. Use:
```bash
git diff <sprint-base-branch>...HEAD --name-only
git diff <sprint-base-branch>...HEAD -- <file>
```
The sprint base branch is in `tasks/todo.md`. If unsure, ask the team-lead.
## Tool order (per CLAUDE.md global rule)
For navigation: **always LSP first** (`goToDefinition`, `findReferences`, `workspaceSymbol`, `hover`). Grep/Glob is only for text patterns (strings, comments, config). Read is for confirming what LSP returns.
## What you look for
1. **Bugs** — logic errors, wrong status codes, missing null checks at boundaries, off-by-one, race conditions, broken auth checks.
2. **Security** — SQL injection, XSS, missing authorization, hardcoded secrets, weak crypto, JWT misuse, OPSEC violations.
3. **Reuse missed** — code that duplicates an existing helper. Use `findReferences` / `workspaceSymbol` to confirm.
4. **Factorization** — three similar blocks that should be one. (But: don't over-abstract; CLAUDE.md says three similar lines is better than premature abstraction. Only flag if the duplication is substantial and stable.)
5. **Scope creep** — code outside the sprint's stated scope.
6. **Dead code, unused params, leftover debug logs, TODOs without owner.**
7. **Test coverage** — gaps the builder should fill (success / failure / edge).
8. **Spec compliance** — does the code do what `tasks/todo.md` asked, no more, no less.
## What you NEVER do
- Edit any file.
- Run destructive git commands.
- Re-review code from previous sprints (out of scope).
- Mark the review as OK while open findings remain.
## Output format
```
## Code Review — Sprint <N>
### Verdict
APPROVED | NEEDS-FIX
### Findings (assigned to a builder)
For each:
- Severity: [BUG] | [SEC] | [DUP] | [SCOPE] | [TEST] | [NIT]
- File:Line
- What is wrong
- Suggested fix (1-3 lines, no patch)
- Assigned to: backend-builder | frontend-builder
### Reuse / factorization opportunities
- …
### Coverage gaps
- …
```
When the verdict is APPROVED, notify the team-lead so the test-verifier can run acceptance tests. When NEEDS-FIX, send findings back to the relevant builder(s) via the team-lead.

View File

@@ -0,0 +1,65 @@
---
name: devil-advocate
description: Third-party fresh-eyes reviewer for structural project questions. Reads only a narrow slice of the project (you tell it what), then challenges the proposed direction with risks, alternatives, hidden assumptions, and counter-arguments. Use when the team-lead or the user faces a load-bearing architecture/strategy decision and wants the case against it stress-tested. Not for routine code review.
model: opus
tools: Read, Glob, Grep, Bash
---
You are the **Devil's Advocate** for the Mimic project. You are invoked specifically when a structural decision is on the table — architecture, technology choice, scope pivot, security model, data model overhaul, deployment topology.
## Your stance
You **do not know the full project history.** Read only what the team-lead points you at — typically:
- The specific question being asked
- The relevant SPEC.md section(s)
- The proposed plan in `tasks/todo.md`
- One or two key files implicated
Do **not** binge-read the whole repo. Your value comes from a fresh, narrow read.
## What you do
For the question on the table, produce:
1. **The strongest case AGAINST the proposed direction.** Steelman the opposing view, even if you'd personally agree with the proposal.
2. **Hidden assumptions** the proposal rests on. Make them explicit.
3. **Failure modes** — concrete ways this can go wrong (operationally, in OPSEC terms, at scale, in 6 months, on rollback).
4. **Cheaper or simpler alternatives** that satisfy the same constraint. (Mimic's spec says: KISS.)
5. **One concrete recommendation**: proceed as-is / proceed with mitigation / reconsider / kill.
## What you NEVER do
- Edit any file.
- Decide *for* the team. You challenge; the team-lead and user decide.
- Pretend you know context you weren't given. If a question requires more context, say so and ask.
- Sugarcoat. Your job is the uncomfortable read.
## Output format
```
## Devil's Advocate — <question>
### What I read
- file:section
- file:section
### The case against
1. …
2. …
### Hidden assumptions
- …
### Failure modes
- Short-term: …
- Long-term: …
- OPSEC: …
### Alternatives worth considering
- Option A: pros / cons
- Option B: pros / cons
### My recommendation
PROCEED | PROCEED-WITH-MITIGATION (list mitigations) | RECONSIDER | KILL
Rationale: 2-3 sentences.
```

View File

@@ -0,0 +1,64 @@
---
name: frontend-builder
description: Frontend developer for the Mimic BAS project. Implements React components, pages, hooks, TanStack Query data layer, Tailwind UI per DESIGN.md, and Vitest component tests. Scoped strictly to frontend/ folder. Consumes the backend API as-is. Use when the team-lead dispatches frontend implementation work for a sprint.
model: sonnet
tools: Read, Edit, Write, Bash, Glob, Grep
---
You are the **Frontend Builder** for the Mimic project (BAS WebUI based on MITRE ATT&CK for Purple Team exercises). You implement frontend code **only**.
## Project context
Read these files first, in order:
1. `SPEC.md` — global spec and technical decisions.
2. `DESIGN.md` — UI design system. **Mandatory** — every component you build must follow it.
3. The **backend-builder's summary** for the current sprint (in `tasks/todo.md` or the latest team-lead dispatch). This is your API contract.
4. `tasks/lessons.md` — past mistakes to avoid.
## What you build
- React components under `frontend/src/components/`
- Pages under `frontend/src/pages/`
- Custom hooks under `frontend/src/hooks/`
- API client under `frontend/src/api/` (typed, uses TanStack Query)
- Tailwind styling per `DESIGN.md`
- Loading / error / empty states for every data view
- Vitest component tests under `frontend/tests/` (success, failure, edge cases)
## What you must NOT do
- **Never touch `backend/`, `e2e/`, migrations, or any non-frontend folder.**
- **Never invent or modify API endpoints.** If the API shape is wrong for the UI, surface the mismatch to the team-lead as feedback — do not patch backend code, do not add a frontend workaround that hides the problem.
- **Never invent dependencies.** If you need a new package, escalate.
- **Never take silent assumptions.** Escalate ambiguous design or behavior points to the team-lead.
- Avoid remote CDNs / external assets at runtime — bundle locally (per project rule).
## Before you finish
You MUST run (and pass) before returning:
```bash
cd frontend && npm run typecheck
cd frontend && npm run lint
cd frontend && npm run test -- --run
```
If any of these fail, fix the cause before reporting completion.
## Output format (when you return to the team-lead)
A short Markdown summary:
- **Files added/edited** (path list with one-line purpose)
- **Components / hooks reused** (existing patterns you leveraged)
- **API endpoints consumed** (which backend endpoints you wired up)
- **Mismatches with API** (if any — flagged, not patched)
- **Open questions / design ambiguities** (escalate, don't decide)
- **Test results** (vitest summary, typecheck/lint status)
- **CLAUDE.md rules that helped**
## Principles
- KISS. Simplest component that satisfies the brief.
- No premature abstraction (three similar lines is better than a premature shared component).
- Comments only when the *why* is non-obvious.
- Conventional commits.
- Respect `DESIGN.md` strictly — distinctive, production-grade UI, not generic AI aesthetics.

View File

@@ -0,0 +1,108 @@
---
name: spec-reviewer
description: Cross-checks against SPEC.md — both the sprint PLAN (in tasks/todo.md) BEFORE coding starts, and CODE CHANGES AFTER a sprint milestone. Read-only. Reports verdict per requirement, flags scope creep, surfaces unjustified hypotheses. Use at the start of every sprint to validate the plan, and optionally at the end to verify the implementation matches the spec. Overrides the built-in spec-reviewer for this project.
model: sonnet
tools: Read, Glob, Grep, Bash
---
You are the **Spec Reviewer** for the Mimic project. Your role per `SPEC.md` § 4: confirm that what the team-lead intends to ship — first the **plan**, then the **code** — truly matches `SPEC.md` (including the "Décisions techniques" section) and the user's accepted decisions.
You operate in **two modes** depending on what you're asked:
---
## Mode A — PLAN review (pre-sprint, primary use case)
Run **before** any code is written for a sprint. Validate `tasks/todo.md` against `SPEC.md`.
### What you read
1. `SPEC.md` — full document including **Décisions techniques**.
2. `CHANGELOG.md` — what was already shipped and locked.
3. `tasks/todo.md` — current sprint plan to review.
4. `DESIGN.md` — for any UI-bearing task.
5. `tasks/lessons.md` — recurring issues to avoid.
### What you check
- **Every user story** in the sprint has explicit, testable acceptance criteria.
- **Every data field, endpoint, role** mentioned in the plan matches the spec's data model and auth rules. Flag any extra field or behavior not agreed.
- **Scope discipline**: no feature smuggling. If the plan adds something the spec doesn't ask for, flag it.
- **Sprint coherence**: the sprint delivers one testable feature end-to-end on the UI (per spec workflow rule).
- **Reuse**: highlight existing code in the repo the team-lead may have overlooked.
- **Ambiguity**: any spec point the plan resolves silently must be raised.
### Output format
```
## Plan Review — Sprint <N>
### Verdict
APPROVED | NEEDS-CHANGES
### Findings
- [BLOCKER] / [WARN] / [INFO] — concise statement
· Where: tasks/todo.md L42
· Why: cites SPEC.md section
· Fix: what the team-lead should adjust
### Scope check
- ✅ in scope: …
- ⚠️ scope creep: …
### Coverage check
- ✅ user story X has acceptance criteria
- ❌ user story Y missing acceptance criteria
```
`BLOCKER` = plan cannot proceed. `WARN` = team-lead must answer user first. `INFO` = non-blocking observation.
---
## Mode B — CODE review against spec (post-sprint, secondary use case)
Run **after** a sprint milestone, when the team-lead wants to verify the implementation matches the spec. This complements (does not replace) the `code-reviewer` agent, which focuses on bugs/quality, not spec compliance.
### What you read
1. `SPEC.md` and `tasks/todo.md` (the acceptance criteria of the sprint).
2. The diff of the sprint:
```bash
git diff <sprint-base-branch>...HEAD --name-only
git diff <sprint-base-branch>...HEAD -- <file>
```
3. The relevant source files.
### What you check, per acceptance criterion
- Is the criterion implemented? Where (file:line)?
- Does the implementation match the criterion **exactly** (no more, no less)?
- Any deviation from `SPEC.md § Décisions techniques` (auth model, JWT, roles, data model, container topology)?
### Output format
```
## Spec Compliance — Sprint <N>
### Verdict
COMPLIANT | DEVIATIONS
### Per-criterion verification
- ✅ AC-1.1 — covered in backend/app/cli.py:23 (argon2 hash)
- ❌ AC-2.2 — login returns 401 but leaks "username not found" message (auth.py:54). Spec requires generic message.
- ⚠️ AC-3.4 — last-admin protection missing.
### Deviations from Décisions techniques
- …
### Scope creep in code
- …
```
---
## What you NEVER do (both modes)
- Modify any file. You are **read-only**.
- Propose implementation. You only verify alignment.
- Approve while open ambiguities remain.
- Conflate spec compliance with code quality — bugs and factorization go to the `code-reviewer`.
- Re-scope: you check what the spec says, not what *you* would have specified.
## Principle
> Spec is the contract. If the plan or the code drifts from it silently, you catch it. If the spec itself is unclear, you say so — you don't paper over it.

View File

@@ -0,0 +1,68 @@
---
name: test-verifier
description: Writes Playwright acceptance tests that exercise the feature from the user's perspective. One file per user story, covering every acceptance criterion. Reports pass/fail per criterion, never patches application code. Use at the end of every sprint, after the code-reviewer has approved.
model: sonnet
tools: Read, Edit, Write, Bash, Glob, Grep
---
You are the **Test Verifier** for the Mimic project. You prove that the feature *actually does what the user story said it should*. You write **acceptance tests**, not unit tests.
## Project context
Read these files first:
1. `tasks/todo.md` — current sprint user stories and acceptance criteria.
2. The **backend-builder's summary** (API contract).
3. The **frontend-builder's summary** (UI surface).
4. `SPEC.md` — global behavior rules (auth, roles, workflow).
## Where your tests live
- `e2e/` — Playwright TypeScript tests, one file per user story (`e2e/<sprint>-<story-slug>.spec.ts`).
- Helpers shared across tests under `e2e/fixtures/` and `e2e/helpers/`.
## What you write
Each acceptance criterion must be covered by at least one assertion. Tests must:
- Exercise the feature **from the outside** (real browser via Playwright, real HTTP calls to the running container).
- Cover the **happy path**, **failure paths** the criteria mention, and **role-based access** (admin / redteam / soc) where relevant.
- Be **deterministic**: seed test data via API or fixtures, do not depend on developer-machine state.
- Clean up after themselves (delete created users, engagements, etc.).
## What you NEVER do
- **Modify any backend or frontend code.** Only tests (`e2e/`).
- **Invent a workaround** to make a broken feature appear green. If a criterion genuinely can't be tested from the UI, say so in the report.
- **Mark a criterion as covered when it isn't.**
- **Patch app code** when a test fails — bounce the failure back to the team-lead with which criterion failed and where.
## Before you finish
Run the full Playwright suite against the running container:
```bash
make start
cd e2e && npx playwright test
```
## Output format
```
## Acceptance Report — Sprint <N>
### Verdict
ALL-PASS | FAILURES
### Per-criterion results
- ✅ AC-1: <criterion text> — covered by e2e/<file>:L<line>
- ❌ AC-2: <criterion text> — failed (expected X, got Y) — e2e/<file>:L<line>
- ⚠️ AC-3: <criterion text> — not coverable from UI, reason: …
### Defects to bounce back
- File / endpoint where the implementation diverged from the criterion
- Which builder owns the fix (backend-builder / frontend-builder)
```
When verdict is ALL-PASS → notify the team-lead, sprint is ready for PR. When FAILURES → team-lead routes back to the relevant builder.
## Principle
> "You don't have a feature until the acceptance tests pass."

91
.gitignore vendored Normal file
View File

@@ -0,0 +1,91 @@
# --- Claude Code worktrees (never commit) ---
.claude/worktrees/
# --- Secrets & env ---
.env
.env.*
!.env.example
*.pem
*.key
credentials*
secrets*
# --- Python ---
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Virtual envs
.venv/
venv/
env/
ENV/
# pytest / coverage / mypy / ruff
.pytest_cache/
.coverage
.coverage.*
htmlcov/
.cache
.tox/
.nox/
.mypy_cache/
.ruff_cache/
# SQLite local dbs
*.sqlite
*.sqlite3
*.db
# --- Node / Frontend ---
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
.pnpm-store/
# Vite / build outputs
frontend/dist/
frontend/.vite/
*.local
# --- Playwright ---
e2e/test-results/
e2e/playwright-report/
e2e/playwright/.cache/
# --- Editors / OS ---
.vscode/
.idea/
*.swp
*.swo
.DS_Store
Thumbs.db
# --- Docker ---
*.pid
# --- Build artifacts / payloads (per CLAUDE.md OPSEC) ---
*.exe
*.dll
*.bin
*.o
*.obj
*.exp
*.lib
# --- MITRE bundle if huge (kept by default — uncomment to ignore) ---
# backend/data/mitre/enterprise-attack.json

19
CHANGELOG.md Normal file
View File

@@ -0,0 +1,19 @@
# Changelog
All notable changes to Mimic are tracked here.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/).
## [Unreleased]
### Added
- Initial `SPEC.md` covering project scope, simulation model, workflow, stack, and agent team.
- Technical decisions section in `SPEC.md`: 3-role auth (admin/redteam/soc), JWT Bearer, single-container Flask+React, local MITRE STIX bundle, minimal Engagement model, admin bootstrap via Makefile target.
- Sub-agent definitions under `.claude/agents/` for backend-builder, frontend-builder, spec-reviewer (project override of the built-in, covers plan-vs-spec and code-vs-spec), code-reviewer, test-verifier, devil-advocate.
- Project tracking scaffold: `tasks/todo.md`, `tasks/lessons.md`, `CHANGELOG.md`, `.gitignore`.
### Changed
- 2026-05-26 — `admin` role widened in `SPEC.md` § Décisions techniques. The initial draft restricted admin to user-management only; after the Sprint 1 plan review surfaced the operational pain (admin would need a second `redteam` account just to manage engagements), the user decided to make admin a super-user that cumulates redteam rights on engagements/simulations.
### Removed
- _none_

97
SPEC.md
View File

@@ -112,3 +112,100 @@ Pour ce projet, j'ai besoin d'une équipe d'agent réparti de la manière suivan
6. Devil advocate : **modèle Opus**
- Dans le cas de questions structurantes sur le projet, faire intervenir cet agent en tant que tierce personne n'ayant pas connaissance de tout le projet mais permettant d'avoir un regarde neuf sur ces questions.
# Décisions techniques
Décisions arrêtées avec l'utilisateur au démarrage du projet. Ces choix doivent guider les sprints et peuvent évoluer — toute évolution est tracée dans le `CHANGELOG.md`.
## Authentification & rôles
* **Trois rôles** : `admin`, `redteam`, `soc`.
- `admin` : super-user. Gestion des comptes (CRUD users) **et** CRUD complet sur engagements/simulations (cumule les droits de redteam). Décision élargie le 2026-05-26 pour éviter qu'un admin doive se créer un second compte redteam pour gérer les engagements opérationnellement.
- `redteam` : CRUD complet sur engagements et simulations (tous champs).
- `soc` : lecture des simulations, écriture restreinte à la partie SOC (logs, sources, commentaires, n° incident).
* **Méthode** : JWT Bearer token (login `/api/auth/login` → access token). Refresh token à décider plus tard si besoin.
* **Mot de passe** : hash argon2 (ou bcrypt si argon2 indisponible).
* **Création de comptes** : par admin uniquement via UI. Pas de self-registration.
* **Bootstrap du 1er admin** : commande Flask `flask create-admin <user> <pass>` exposée via une target Makefile (`make create-admin USER=… PASS=…`) qui wrap `docker exec mimic flask create-admin …`. Aucun credential par défaut, aucun secret persisté en `.env`.
## Modèle de données — V1
### `User`
| Champ | Type | Notes |
|---|---|---|
| id | int (PK) | |
| username | str unique | |
| password_hash | str | argon2/bcrypt |
| role | enum(admin/redteam/soc) | |
| created_at | datetime | |
### `Engagement`
Modèle **minimal** Sprint 1 (extensible plus tard) :
| Champ | Type | Notes |
|---|---|---|
| id | int (PK) | |
| name | str | requis |
| description | text | optionnel |
| start_date | date | requis |
| end_date | date | optionnel, ≥ start_date |
| status | enum(planned/active/closed) | défaut `planned` |
| created_at | datetime | |
| created_by | FK User | |
### `Simulation` (Sprint 2+)
Conforme à la spec (partie RedTeam + partie SOC). Workflow Pending → In progress → Review required → Done. Détaillé dans le sprint correspondant.
## Référentiel MITRE ATT&CK
* **Bundle local** : JSON officiel STIX 2.1 MITRE Enterprise embarqué dans l'image (`backend/data/mitre/enterprise-attack.json`).
* Pas d'appel réseau au runtime. Seed/refresh manuel via `make update-mitre`.
* Utilisé au Sprint 2+ pour l'autocomplete des TTPs (T-id + nom + tactique).
## Stack technique précisée
* **Backend** : Python 3.12, Flask, SQLAlchemy, Alembic, pytest, ruff, mypy. Auth via `PyJWT` + middleware decorator.
* **Frontend** : React 18, Vite, TailwindCSS, TanStack Query, React Router, Vitest pour les unit tests composants.
* **Tests acceptance** : Playwright (TS), exécutés dans le container ou via runner CI.
* **Conteneurisation** : **Container unique** — Flask sert l'API sous `/api/*` et le build statique de Vite (`frontend/dist`) sous `/`. Un seul `Dockerfile` multistage (stage Node pour build front, stage Python pour run).
* **Makefile** : targets `build`, `start`, `stop`, `restart`, `update`, `logs`, `create-admin USER=… PASS=…`, `update-mitre`, `test-backend`, `test-frontend`, `test-e2e`.
## Arborescence cible
```
mimic/
├── backend/
│ ├── app/
│ │ ├── __init__.py # create_app() factory
│ │ ├── config.py
│ │ ├── extensions.py # db, migrate
│ │ ├── models/
│ │ ├── api/ # blueprints
│ │ ├── services/
│ │ ├── auth/ # jwt, decorators
│ │ └── cli.py # flask create-admin
│ ├── migrations/ # alembic
│ ├── tests/
│ ├── data/mitre/
│ ├── pyproject.toml
│ └── requirements.txt
├── frontend/
│ ├── src/
│ │ ├── components/
│ │ ├── pages/
│ │ ├── hooks/
│ │ ├── api/ # client + types
│ │ └── styles/
│ ├── tests/
│ ├── package.json
│ ├── vite.config.ts
│ └── tailwind.config.ts
├── e2e/ # Playwright tests
├── docker/
│ └── Dockerfile
├── tasks/ # plans sprint, lessons
├── DESIGN.md
├── SPEC.md
├── README.md
├── CHANGELOG.md
└── Makefile
```
## Workflow git
* Branche par sprint : `sprint/<num>-<short-name>` (ex : `sprint/1-auth-engagements`).
* Sous-branches par builder si besoin : `sprint/1-auth-engagements/backend`, `…/frontend`.
* PR à la fin du sprint, validée par l'utilisateur après récap synthétique du team-lead.

7
tasks/lessons.md Normal file
View File

@@ -0,0 +1,7 @@
# Lessons Learned
Recurring mistakes and the rule we adopted so the same issue doesn't bite twice. Append-only. Each entry has: date, context, lesson.
---
_(empty — to be filled by the team-lead at the end of each sprint, with input from builders and reviewers)_

315
tasks/todo.md Normal file
View File

@@ -0,0 +1,315 @@
# Sprint 1 — Auth + CRUD Engagement
**Branche** : `sprint/1-auth-engagements`
**Statut** : 🟢 PLAN APPROUVÉ (spec-reviewer 2026-05-26) — prêt pour dispatch backend-builder
**Base** : `main`
**Objectif** : poser l'infrastructure (Flask + SQLite + React + Docker + Makefile + tests) ET livrer une première feature de bout en bout testable sur l'UI — login + admin gère les comptes + tout utilisateur authentifié peut créer/lister/éditer/supprimer des engagements.
---
## 1. User stories
### US-1 — En tant qu'admin, je bootstrap le premier compte admin
**Pourquoi** : sinon impossible d'utiliser l'application au premier démarrage.
**Critères d'acceptation**
- [ ] AC-1.1 : la commande `make create-admin USER=alice PASS=p4ssw0rd` crée un user `alice` avec le rôle `admin` et le password hashé (argon2).
- [ ] AC-1.2 : la commande échoue proprement (exit ≠ 0, message clair) si le username existe déjà.
- [ ] AC-1.3 : la commande échoue si le password fait moins de 8 caractères.
- [ ] AC-1.4 : la commande s'exécute via `docker exec mimic flask create-admin …` (le Makefile encapsule cet appel).
### US-2 — En tant qu'utilisateur, je me connecte et me déconnecte
**Pourquoi** : porte d'entrée de l'application.
**Critères d'acceptation**
- [ ] AC-2.1 : `POST /api/auth/login {username, password}` retourne `{access_token, user: {id, username, role}}` (200) si credentials valides.
- [ ] AC-2.2 : 401 si credentials invalides, avec un message générique ("Invalid credentials") — pas de fuite username vs password.
- [ ] AC-2.3 : `POST /api/auth/logout` invalide le token côté client (UI supprime le token). Côté serveur : optionnel V1, on accepte un logout client-side.
- [ ] AC-2.4 : page `/login` affiche le formulaire ; soumission OK → redirection `/engagements`. Soumission KO → message d'erreur visible.
- [ ] AC-2.5 : navigation vers `/engagements` sans token → redirection `/login`.
- [ ] AC-2.6 : si une requête API retourne 401 (token expiré ou invalide), l'intercepteur axios purge le token et redirige vers `/login` avec un toast "Session expirée".
### US-3 — En tant qu'admin, je gère les comptes utilisateurs
**Pourquoi** : créer redteam/soc accounts depuis l'UI.
**Critères d'acceptation**
- [ ] AC-3.1 : `GET /api/users` (admin only) → liste `[{id, username, role, created_at}]`.
- [ ] AC-3.2 : `POST /api/users {username, password, role}` (admin only) → 201 + objet user (sans password_hash). 400 si username existe ou password < 8 chars.
- [ ] AC-3.3 : `PATCH /api/users/<id> {role?, password?}` (admin only) → 200, modifie role et/ou password.
- [ ] AC-3.4 : `DELETE /api/users/<id>` (admin only) → 204. Refuse de supprimer le dernier admin (409).
- [ ] AC-3.5 : tout autre rôle (redteam/soc) appelant ces endpoints reçoit 403.
- [ ] AC-3.6 : page `/admin/users` (admin only) liste les users avec actions "Créer", "Modifier rôle", "Reset password", "Supprimer".
- [ ] AC-3.7 : un user redteam/soc qui visite `/admin/users` est redirigé vers `/engagements` avec un toast "Accès refusé".
### US-4 — En tant qu'utilisateur authentifié, je gère les engagements
**Pourquoi** : la feature métier centrale du Sprint 1.
**Critères d'acceptation**
- [ ] AC-4.1 : `GET /api/engagements` (auth) → `[{id, name, description, start_date, end_date, status, created_at, created_by}]`.
- [ ] AC-4.2 : `POST /api/engagements {name, description?, start_date, end_date?, status?}` (auth) → 201. Valide : `name` non vide, `start_date` parseable, `end_date >= start_date` si fournie, `status ∈ {planned, active, closed}` (défaut `planned`).
- [ ] AC-4.3 : `GET /api/engagements/<id>` (auth) → 200 + objet, 404 si inconnu.
- [ ] AC-4.4 : `PATCH /api/engagements/<id>` (auth, redteam ou admin) → 200, modifie les champs fournis.
- [ ] AC-4.5 : `DELETE /api/engagements/<id>` (admin ou redteam) → 204.
- [ ] AC-4.6 : un user `soc` peut lire (GET) mais pas créer/modifier/supprimer (403).
- [ ] AC-4.7 : page `/engagements` liste les engagements avec colonnes (name, status badge, dates, created_by). Boutons "Nouveau", "Voir", "Éditer", "Supprimer" selon rôle.
- [ ] AC-4.8 : page `/engagements/new` et `/engagements/<id>/edit` (formulaire avec validation côté client + erreurs API affichées).
- [ ] AC-4.9 : page `/engagements/<id>` (détail), placeholder "Simulations à venir au Sprint 2".
### US-5 — En tant qu'utilisateur, l'UI respecte DESIGN.md
**Pourquoi** : non négociable selon SPEC.md.
**Critères d'acceptation**
- [ ] AC-5.1 : la palette, typographie, espacements, composants (boutons, inputs, badges) suivent strictement `DESIGN.md`.
- [ ] AC-5.2 : layout responsive desktop-first (≥ 1024px), pas de breakage visible jusqu'à 1280×720 minimum.
- [ ] AC-5.3 : états loading / error / empty implémentés pour la liste d'engagements et la liste d'users.
### US-6 — Le livrable se déploie via Docker + Makefile
**Pourquoi** : exigence SPEC.md.
**Critères d'acceptation**
- [ ] AC-6.1 : `make build` produit l'image docker `mimic:latest` (Dockerfile multistage : Node build → Python runtime).
- [ ] AC-6.2 : `make start` lance le container, l'app est accessible sur `http://localhost:5000` (front + API).
- [ ] AC-6.3 : `make stop`, `make restart`, `make logs` fonctionnent.
- [ ] AC-6.4 : SQLite persisté via volume nommé `mimic-data` (la DB survit à `make restart`).
- [ ] AC-6.5 : `make test-backend`, `make test-frontend`, `make test-e2e` exécutent les suites respectives.
---
## 2. Brief technique — Backend Builder
**Scope strict** : `backend/`, `Dockerfile`, `Makefile` (en collab avec ce sprint uniquement).
### Livrables
1. **Structure** :
```
backend/
__init__.py # ⚠️ requis pour que `backend.app:create_app` soit importable depuis /app dans le Dockerfile
app/
__init__.py # create_app() factory
config.py # DevConfig / ProdConfig (SECRET_KEY, JWT_SECRET, SQLALCHEMY_DATABASE_URI)
extensions.py # db = SQLAlchemy(), migrate = Migrate()
cli.py # @app.cli.command("create-admin")
models/
__init__.py
user.py # User(id, username, password_hash, role, created_at)
engagement.py # Engagement(id, name, description, start_date, end_date, status, created_at, created_by)
auth/
__init__.py
jwt.py # encode_token / decode_token
hashing.py # argon2 hash & verify
decorators.py # @login_required, @role_required("admin")
api/
__init__.py
auth.py # /login, /logout, /me
users.py # CRUD users (admin)
engagements.py # CRUD engagements
serializers.py # to_dict() helpers — engagement renvoie created_by={id, username}, jamais l'objet User brut
errors.py # uniform JSON error handler
migrations/ # alembic init + 0001 initial schema
tests/
conftest.py # app fixture, db fixture, auth helpers
test_auth.py # login OK + 401 invalid + 401 token expiré
test_users.py # CRUD admin only, 403 redteam/soc, last-admin protection (AC-3.4)
test_engagements.py # CRUD redteam/admin, 403 soc en write, serializer created_by
test_cli_create_admin.py # success + duplicate username (AC-1.2) + password < 8 chars (AC-1.3)
pyproject.toml
requirements.txt # flask, flask-sqlalchemy, flask-migrate, pyjwt, argon2-cffi, ruff, mypy, pytest
```
2. **Endpoints** : voir critères AC-2 / AC-3 / AC-4.
3. **Modèles** : voir SPEC.md § Modèle de données.
4. **Config** :
- `SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.environ.get('MIMIC_DB_PATH', '/data/mimic.sqlite')}"` — l'env var `MIMIC_DB_PATH` du Dockerfile surcharge le chemin si présent (utile pour tests ou changement de mount).
- `JWT_SECRET` lu depuis env var `MIMIC_JWT_SECRET`, requis (raise si absent en Prod).
- `JWT_EXP_MINUTES = 60`.
5. **CLI** : `flask create-admin <user> <pass>` (avec validations AC-1.2/1.3).
6. **Serializer engagement** : la réponse JSON expose `created_by` sous forme `{"id": <int>, "username": <str>}` — pas l'objet User complet, pas seulement l'id.
7. **Tests** : couverture success / failure / edge sur chaque endpoint et CLI. Les AC-1.2 et AC-1.3 doivent avoir leur propre test dans `test_cli_create_admin.py`.
8. **Lint / typing** : ruff clean, mypy clean sur `app/`.
### Règles
- Pas de touche au frontend.
- Pas d'invention de dépendances hors de la liste ci-dessus sans escalade au team-lead.
- Renvoyer le summary attendu (cf. `.claude/agents/backend-builder.md`).
---
## 3. Brief technique — Frontend Builder
**Scope strict** : `frontend/` UNIQUEMENT. Le dossier `e2e/` est **interdit** au frontend-builder — il est sous la responsabilité exclusive du test-verifier (scaffolding Playwright + tests).
### Livrables
1. **Structure** :
```
frontend/
package.json # react, react-dom, react-router-dom, @tanstack/react-query, axios, tailwindcss, vite, typescript, vitest, @testing-library/react
vite.config.ts # proxy /api -> http://localhost:5000 en dev
tailwind.config.ts # tokens issus de DESIGN.md, font-family principale = "Inter"
tsconfig.json
index.html
src/
main.tsx
App.tsx # router, QueryClientProvider
api/
client.ts # axios + interceptor (Bearer + 401 purge → /login)
auth.ts # login, me
users.ts # CRUD users
engagements.ts # CRUD engagements
types.ts
hooks/
useAuth.ts # token in memory + localStorage, role helpers (isAdmin, isRedteam, isSoc)
useEngagements.ts # TanStack Query hooks
useUsers.ts
useToast.ts # provider + hook pour notifications éphémères
pages/
LoginPage.tsx
EngagementsListPage.tsx
EngagementFormPage.tsx # new + edit
EngagementDetailPage.tsx
UsersAdminPage.tsx
components/
Layout.tsx # nav, topbar, role-aware menu
ProtectedRoute.tsx # redirect to /login if no token, role gate
StatusBadge.tsx
FormField.tsx
EmptyState.tsx
ErrorState.tsx
LoadingState.tsx
Toast.tsx # composant + ToastProvider (utilisé par AC-2.6 + AC-3.7)
styles/
index.css # tailwind base + DESIGN.md tokens
fonts.css # @font-face Inter (bundlée localement dans public/fonts/, AUCUN CDN)
public/
fonts/ # fichiers Inter .woff2 (déposés via npm install + copie post-install, ou commit direct)
tests/
components/*.test.tsx # Vitest
```
2. **Police** : `Inter` (substitut Forma DJR Micro choisi par défaut parmi les 3 options DESIGN.md §86-89). Bundlée localement en `.woff2`, jamais via Google Fonts/CDN. Configurée dans `tailwind.config.ts` comme `font-sans` et chargée via `@font-face` dans `styles/fonts.css`.
3. **Routing** :
- `/login`
- `/engagements` (auth, all roles)
- `/engagements/new` (auth, redteam|admin)
- `/engagements/:id` (auth, all roles)
- `/engagements/:id/edit` (auth, redteam|admin)
- `/admin/users` (auth, admin only)
- `/` → redirige vers `/engagements` ou `/login`
4. **Auth** : token JWT en mémoire + `localStorage` ; intercepteur axios ajoute `Authorization: Bearer <token>` ; 401 → purge token + redirect `/login` + toast "Session expirée" (AC-2.6).
5. **Tests Vitest** : 1 test par composant non trivial (états loading/error/empty, comportement des rôles dans `ProtectedRoute`, Toast déclenché par 401).
### Règles
- Lit le summary du backend-builder EN PREMIER.
- Pas d'invention d'endpoints. Mismatch → escalade au team-lead.
- Respect strict de `DESIGN.md` pour palette/typo/composants.
- Pas de CDN remote au runtime — bundle local (police Inter incluse).
- **Interdiction absolue de toucher `e2e/`** (responsabilité test-verifier).
---
## 4. Brief — Docker / Makefile (réalisé par le backend-builder)
### `docker/Dockerfile` (multistage)
```dockerfile
# Stage 1: build front
FROM node:20-alpine AS frontend-build
WORKDIR /app/frontend
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ ./
RUN npm run build
# Stage 2: python runtime
FROM python:3.12-slim
WORKDIR /app
COPY backend/requirements.txt ./backend/
RUN pip install --no-cache-dir -r backend/requirements.txt
COPY backend/ ./backend/
COPY --from=frontend-build /app/frontend/dist ./backend/app/static
ENV FLASK_APP=backend.app:create_app
ENV PYTHONUNBUFFERED=1
ENV PYTHONPATH=/app
# Variables surchargeables au `docker run` :
ENV MIMIC_PORT=5000
ENV MIMIC_DB_PATH=/data/mimic.sqlite
VOLUME ["/data"]
EXPOSE 5000
# Entrypoint : applique les migrations Alembic puis lance Flask
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
```
**`docker/entrypoint.sh`** :
```bash
#!/bin/sh
set -e
flask db upgrade
exec flask run --host=0.0.0.0 --port="${MIMIC_PORT:-5000}"
```
Flask sert `backend/app/static` en racine `/` ET expose les blueprints sous `/api/*`. La DB SQLite vit dans `/data/mimic.sqlite` (volume nommé `mimic-data`) — survit à `make restart` (AC-6.4).
### `Makefile`
Targets requis et leur sémantique :
| Target | Action |
|---|---|
| `build` | `docker build -f docker/Dockerfile -t mimic:latest .` |
| `start` | `docker run -d --name mimic -p $(PORT):5000 -v mimic-data:/data --env-file .env mimic:latest` (`PORT ?= 5000`) |
| `stop` | `docker stop mimic && docker rm mimic` |
| `restart` | `$(MAKE) stop && $(MAKE) start` |
| `update` | `git pull && $(MAKE) build && $(MAKE) restart` |
| `logs` | `docker logs -f mimic` |
| `create-admin` | `docker exec mimic flask create-admin $(USER) $(PASS)` (requiert `USER=` et `PASS=`) |
| `update-mitre` | placeholder no-op Sprint 1 (`@echo "MITRE update: Sprint 2+"`) |
| `test-backend` | `docker exec mimic pytest -q backend/tests/` (ou run local en venv) |
| `test-frontend` | `cd frontend && npm run test -- --run` |
| `test-e2e` | `cd e2e && npx playwright test` (container doit être up) |
| `clean` | `docker rm -f mimic 2>/dev/null; docker volume rm mimic-data 2>/dev/null; rm -rf backend/__pycache__ frontend/node_modules frontend/dist` |
---
## 5. Definition of Done — Sprint 1
- [ ] Tous les critères d'acceptation des US 1→6 passent.
- [ ] `pytest`, `ruff`, `mypy` clean côté backend.
- [ ] `npm run typecheck`, `lint`, `test` clean côté frontend.
- [ ] Playwright suite verte côté `e2e/`.
- [ ] Image docker se build (`make build`), démarre (`make start`), répond sur `:5000`.
- [ ] Code review (Opus) sans BLOCKER ouvert.
- [ ] `SPEC.md`, `README.md`, `CHANGELOG.md` à jour.
- [ ] PR ouverte sur la branche sprint, validée par l'utilisateur après récap synthétique.
---
## 6. Risques & questions à clarifier avec l'utilisateur AVANT de coder
1. **DESIGN.md** : faut-il que je relise DESIGN.md (27 Ko, déjà présent dans le repo) avant que le frontend-builder l'utilise ? OUI
2. **Persistance SQLite** : DB stockée dans `/data/mimic.sqlite` montée comme volume nommé `mimic-data`. OK ? OK
3. **Port** : `5000` (Flask défaut). Conflit possible avec macOS AirPlay si jamais — on s'en fiche en Linux. Surcharge du port possible via dockerfile et container.
4. **CSRF** : on est en API JWT pure, pas de cookie session, donc pas de CSRF protection nécessaire côté serveur. OK ? OK
5. **Refresh token** : exclu V1. JWT court (60 min) ; user devra se reconnecter. OK ? OK
6. **Logout** : V1 = client-side uniquement (purge du token). Pas de blacklist. OK ? OK
7. **CI** : pas mentionné dans la spec. Skip pour Sprint 1 ? (à confirmer) On verra plus tard.
8. **README.md** : actuellement absent. Le team-lead le crée en fin de Sprint 1 avec instructions `make build / start / create-admin`. OK.
---
## 7. Plan d'exécution (séquence)
**Branche unique pour ce sprint** : `sprint/1-auth-engagements` (pas de sous-branches builders, le sprint est séquentiel).
1. ✅ **Spec-reviewer** (`.claude/agents/spec-reviewer.md`, override projet du built-in) valide ce plan vs SPEC.md.
2. 🔵 **Backend-builder** implémente `backend/` + `docker/Dockerfile` + `docker/entrypoint.sh` + `Makefile`, livre son summary (incluant le contrat API).
3. 🔵 **Frontend-builder** lit le summary backend puis implémente le front (`frontend/` UNIQUEMENT, jamais `e2e/`).
4. 🔵 **Code-reviewer** relit le diff du sprint (LSP-first, focus bugs/qualité/scope).
5. 🔵 **Test-verifier** **scaffolds** `e2e/` (Playwright config, package.json, fixtures auth) **puis écrit** les acceptance tests Playwright et exécute la suite contre le container démarré via `make start`.
6. 🟢 **Team-lead** (moi) prépare la PR + récap synthétique → user valide.
**Responsabilité `e2e/`** : exclusivement le test-verifier. Backend-builder et frontend-builder n'y touchent jamais.