Aller au contenu

Ingestion (ING) — UX/UI Design Spec

Module: ingestion (ING) · Side: [OPERATOR] only · Version target: [V0.0.0] (forward-looking notes for [V0.0.3] and [V1]) · Date: 2026-05-22

Source PRD: docs/prd/ingestion-prd.md — V0 envelope (§ V0 Envelope Scope) + V1 spec. Architecture: docs/architecture/ingestion-arch.md (cron, MinIO inbox/processed/rejected/duplicate, all-or-nothing commit FSM). Stories: docs/stories/v0.0.0/ingestion/ING-001..006.md — all six v0.0.0 stories are [BACKEND] and individually flag Wireframe: N/A — backend only.


0. TL;DR for v0.0.0 — there is no ingestion UI yet

ING at v0.0.0 ships zero pixels. The full flow is a backend cron:

operator drops file in MinIO  →  cron @02:00 Africa/Abidjan picks it up
                              →  parse + validate (core columns only)
                              →  all-or-nothing commit OR reject to /rejected/
                              →  audit log emits ING_UPLOAD_RECEIVED / ING_COMMITTED / ING_PARSE_FAILED / ING_DUPLICATE_FILE

The PRD V0 envelope is explicit: v0.0.0 — Pas d'upload UI. v0.0.1 — Pas de feedback opérateur. The operator's only visibility into ingestion at v0.0.0 is the audit-log view rendered by the operator-console (OP) — which is OP's design surface, not ING's.

This document therefore has three purposes for v0.0.0:

  1. Make the no-UI decision explicit so OP designers know what they must surface on ING's behalf.
  2. Lock the French micro-copy library (anomaly codes → user-readable French strings) that the backend emits today and every future ING UI inherits.
  3. Sketch the forward UX for v0.0.3 (upload UI) and V1 (correction workshop) so future slices don't re-litigate the structural decisions.

Phase 3 of the design workflow (/wireframe) is skipped for v0.0.0 — no UI-bearing stories exist.


1. UX decisions locked for v0.0.0

The /design protocol normally interviews the user on non-trivial UX choices. v0.0.0 has only one decision space, resolved below:

Decision Resolution Rationale
Does v0.0.0 surface ingestion to the operator at all? No — backend cron only. PRD V0 envelope § 4 line ING + § 8. v0.0.1 explicitly says "Pas de feedback opérateur".
How does the operator know an import ran? Via the audit-log view in OP-console (not ING's surface). Stories ING-002/004/005 all write audit events; OP renders them. Cross-module concern.
How does the operator know a file was rejected? Via the audit log + the MinIO /<tenantId>/rejected/<ts>__<filename> location + the sidecar .errors.json next to it. ING-004 § "reject path" — sidecar is the machine-readable contract. v0.0.3 promotes this to UI; v0.0.0 leaves it as an Ops/runbook artifact.
What does the cabinet admin need on Day 1 to operate ING? A runbook (out of this design's scope but flagged here): how to upload files to S3/MinIO, file naming convention, what 02:00 Africa/Abidjan means for their workflow. The mental model has to be communicated; design can't substitute for documentation when the UI is intentionally absent.
French micro-copy for the audit events and the sidecar .errors.json Defined in § 4 below, locked now so backend strings ship correctly and v0.0.3 UI inherits them. "If a user needs a manual, the design has failed" — even an Ops user reading audit rows needs French that's specific, not "Une erreur est survenue."

No other v0.0.0 UX decisions exist because there is no surface to decide on.


2. User flow — v0.0.0 (system-driven, no human UI step)

flowchart TD
    A["Cabinet admin uploads file<br/>to MinIO /tenant/inbox/<br/>(out-of-band: SFTP, AWS Console,<br/>or rclone — runbook concern)"] --> B[("File at rest in inbox/")]
    B --> C{"Cron tick<br/>02:00 Africa/Abidjan<br/>(ArtciProdGate check)"}
    C -->|"prod + flag=false"| C1["WARN log<br/>[LEGAL-REVIEW] ARTCI pending<br/>file remains in inbox"]
    C -->|"prod + flag=true<br/>OR non-prod"| D["For each active tenant:<br/>list inbox/"]
    D --> E["SHA-256 hash<br/>+ ING_UPLOAD_RECEIVED audit"]
    E --> F{"Hash seen before<br/>for this tenant?"}
    F -->|Yes| G["Move to duplicate/<br/>ING_DUPLICATE_FILE audit<br/>originating_import_id linked"]
    F -->|No| H["Magic-byte verify<br/>+ parse CSV/XLSX"]
    H -->|"format invalid<br/>OR row > 5000<br/>OR core column missing"| I["Move to rejected/<ts>__<filename><br/>+ sidecar .errors.json<br/>+ ING_PARSE_FAILED audit"]
    H -->|"parse OK<br/>+ all rows pass core validation"| J["Single TX:<br/>upsert customer + contract<br/>+ ingestion_import_row per line<br/>+ ING_COMMITTED audit<br/>+ ImportCommitted → OP"]
    H -->|"≥1 blocking anomaly<br/>(v0.0.0 = all-or-nothing reject)"| I
    J --> K["Move file to processed/"]
    I --> L[("Operator silence —<br/>v0.0.1 still no feedback;<br/>audit log is the only signal")]
    K --> L
    G --> L
    C1 --> L

    style C1 fill:#fff3cd
    style I fill:#fde2e2
    style J fill:#d4edda
    style L fill:#e9ecef

Note on the v0.0.0 reject contract: v0.0.0 has no correction workshop. The PRD V0 envelope says "Validation simplifiée : colonnes core uniquement" — so any blocking anomaly fails the whole file and dumps it to /rejected/ with a sidecar. The operator's recourse is fix the source file and re-drop. The full atelier de correction (per-cell inline editing) lands at V1.

Connectivity behavior (operator side — desktop, online-first)

PRD § C.4 + V1 § 30.3: ING is desktop/online-first. v0.0.0 has no synchronous operator interaction at all (cron only), so there is nothing to drop, nothing to resume, nothing to retry from a client perspective. The only "connectivity" surface is between the backend and MinIO/Postgres, handled by the architecture (IngestionStartupRecovery + idempotent MinIO COPY+DELETE per ING-004/005).


3. Wireframes — v0.0.0

None. Repeating for clarity: every v0.0.0 story is backend-only. Section 5 below sketches forward-state wireframes for v0.0.3 and V1; those are not v0.0.0 deliverables and should not be built until their target version is in flight.


4. French micro-copy library — locked now, used by backend today and future UI

This is the only design artefact that ships value at v0.0.0. Backend emits these strings into the audit log and the .errors.json sidecar; the future upload UI (v0.0.3) and correction workshop (V1) will render the same strings without retranslation.

4.1 Anomaly codes → French human strings

The codes are stable enum values defined by ING-003. Each gets one canonical French phrase. Placeholders {valeur}, {date}, {heure}, {prenom}, {nom}, {ligne} are interpolated at emission time.

Code (enum) Classe Phrase française
FORMAT_NON_SUPPORTE BLOQUANT « Format de fichier non supporté. Seuls les fichiers CSV (UTF-8) et XLSX sont acceptés. »
TROP_DE_LIGNES BLOQUANT « Le fichier contient plus de 5 000 lignes. Divisez-le en plusieurs fichiers. »
FICHIER_TROP_GRAND BLOQUANT « Le fichier dépasse la taille maximale de 5 Mo. »
FICHIER_DEJA_IMPORTE BLOQUANT « Ce fichier a déjà été importé le {date} à {heure} par {prenom} {nom}. »
EN_TETES_MANQUANTS BLOQUANT « En-têtes obligatoires manquants : {valeur}. Téléchargez le modèle pour vérifier. »
EN_TETE_INVALIDE BLOQUANT « La ligne d'en-tête est invalide ou absente. La première ligne doit contenir les noms de colonnes. »
IDENTIFIANT_CLIENT_ABSENT BLOQUANT « Ligne {ligne} : l'identifiant client est obligatoire. »
IDENTIFIANT_CONTRAT_ABSENT BLOQUANT « Ligne {ligne} : l'identifiant contrat est obligatoire. »
NOM_ABSENT BLOQUANT « Ligne {ligne} : le nom est obligatoire. »
PRENOMS_ABSENT BLOQUANT « Ligne {ligne} : les prénoms sont obligatoires. »
TELEPHONE_ABSENT BLOQUANT « Ligne {ligne} : le numéro de téléphone est obligatoire. »
TELEPHONE_INVALIDE BLOQUANT « Ligne {ligne} : le numéro « {valeur} » n'est pas un numéro ivoirien valide. Format attendu : +225 suivi de 10 chiffres. »
MONTANT_ABSENT BLOQUANT « Ligne {ligne} : le montant dû est obligatoire. »
MONTANT_INVALIDE BLOQUANT « Ligne {ligne} : le montant « {valeur} » doit être un nombre entier positif en XOF. »
DATE_ECHEANCE_ABSENTE BLOQUANT « Ligne {ligne} : la date d'échéance est obligatoire. »
DATE_ECHEANCE_INVALIDE BLOQUANT « Ligne {ligne} : la date d'échéance « {valeur} » n'est pas une date valide. Format attendu : JJ/MM/AAAA. »
ECHEANCE_DEPASSEE BLOQUANT (V0.0.2+) « Ligne {ligne} : la date d'échéance {date} est dépassée depuis plus de 30 jours. »
DOUBLON_INTRA_FICHIER BLOQUANT « Ligne {ligne} : ce contrat ({valeur}) apparaît plusieurs fois dans le fichier. Seule la première occurrence est conservée. »
CODE_AGENCE_ABSENT BLOQUANT (V1) « Ligne {ligne} : le code agence est obligatoire. »
CODE_AGENCE_INCONNU BLOQUANT (V1) « Ligne {ligne} : le code agence « {valeur} » n'existe pas. Vérifiez les codes agence dans les paramètres du cabinet. »
CODE_AGENCE_NON_AUTORISE BLOQUANT (V1) « Ligne {ligne} : vous n'êtes pas autorisé à importer pour l'agence « {valeur} ». Contactez l'admin du cabinet. »
DATE_NAISSANCE_ABSENTE BLOQUANT (V1) « Ligne {ligne} : la date de naissance est obligatoire. »
DATE_NAISSANCE_INVALIDE BLOQUANT (V1) « Ligne {ligne} : la date de naissance « {valeur} » est invalide. Format attendu : JJ/MM/AAAA. »
BRANCHE_ABSENTE BLOQUANT (V1) « Ligne {ligne} : la branche est obligatoire. »
EMAIL_ABSENT NON-BLOQUANT (V1) « Ligne {ligne} : email absent (information facultative). »
NUMERO_POLICE_ABSENT NON-BLOQUANT (V1) « Ligne {ligne} : numéro de police absent (information facultative). »
DATE_NAISSANCE_MODIFIEE SURVEILLANCE (V1) « Ligne {ligne} : la date de naissance diffère de celle déjà enregistrée pour ce client. »
DONNEES_CONTRAT_MODIFIEES SURVEILLANCE (V1) « Ligne {ligne} : le montant ou la date d'échéance diffère de celui déjà enregistré pour ce contrat. »

4.2 Status pill labels (v0.0.0 backend writes; v0.0.3+ UI displays)

Status interne Libellé français Couleur sémantique
PICKED_UP En traitement --color-info (bleu calme)
COMMITTED Importé --color-success (vert)
PARSE_FAILED Échec de lecture --color-error (rouge)
DUPLICATE Doublon — déjà importé --color-warning (ambre)
(V1) STAGED_CLEAN En attente de validation --color-info
(V1) STAGED_WITH_ANOMALIES Anomalies à corriger --color-warning
(V1) ABANDONED Annulé --color-text-tertiary (gris)
(V1) EXPIRED Expiré --color-text-tertiary

4.3 Sidecar .errors.json shape (v0.0.0 — operator/Ops-readable)

{
  "filename": "portefeuille_mars_2026.csv",
  "rejected_at": "2026-05-22T02:01:43+00:00",
  "reason_code": "PARSE_FAILED",
  "summary_fr": "Le fichier a été rejeté. 17 ligne(s) présentent des anomalies bloquantes.",
  "anomalies": [
    { "ligne": 3,  "code": "TELEPHONE_INVALIDE",       "champ": "telephone",      "valeur": "07 12 34", "message_fr": "Ligne 3 : le numéro « 07 12 34 » n'est pas un numéro ivoirien valide. Format attendu : +225 suivi de 10 chiffres." },
    { "ligne": 8,  "code": "MONTANT_INVALIDE",         "champ": "montant_du",     "valeur": "-500",     "message_fr": "Ligne 8 : le montant « -500 » doit être un nombre entier positif en XOF." }
  ],
  "next_step_fr": "Corrigez les lignes listées dans le fichier source et re-déposez-le dans le dossier d'import."
}

Two rules:

  • message_fr is the same string the future UI will render — backend and UI must not diverge.
  • next_step_fr is mandatory — every error must point at the next concrete action. "Une erreur est survenue, réessayez" is forbidden.

4.4 Audit-event message templates (rendered by OP-console's audit-log view)

Event code Message rendu en français
ING_UPLOAD_RECEIVED « Fichier « {filename} » détecté dans la boîte d'import ({file_size_bytes} octets). »
ING_COMMITTED « Import « {filename} » validé : {rows_created} créé(s), {rows_updated} mis à jour, {rows_skipped} ignoré(s). »
ING_PARSE_FAILED « Import « {filename} » rejeté ({reason_code}). Voir le rapport d'erreurs joint. »
ING_DUPLICATE_FILE « Fichier « {filename} » déjà importé le {originating_date} — déplacé en doublons. »
ING_CUSTOMER_CREATED « Nouveau client créé : {source_customer_id}. »
ING_CUSTOMER_UPDATED « Client mis à jour : {source_customer_id}. »
ING_CONTRACT_CREATED « Nouveau contrat créé : {source_contract_id}. »
ING_CONTRACT_UPDATED « Contrat mis à jour : {source_contract_id}. »
ING_DOB_DRIFT_DETECTED (V1) « Divergence date de naissance détectée pour le client {source_customer_id} (année {old_year} → {new_year}). »

PII rule: full date of birth never appears in audit. Only the year, per PRD § B.8 minimisation note.


5. Forward-state design — v0.0.3 (upload UI) and V1 (correction workshop)

These sections are forward-looking and not part of the v0.0.0 deliverable. They are documented now so the OP-console design stays compatible.

5.1 v0.0.3 — Upload UI (admin-only, desktop)

5.1.1 Entry point — "Importer un fichier" action in OP-console sidebar

Single page. Two zones:

┌──────────────────────────────────────────────────────────────────┐
│  Imports                                          [Modèle XLSX ↓]│
│  ───────────────────────────────────────────────────────────────│
│                                                                  │
│   ┌──────────────────────────────────────────────────────────┐  │
│   │                                                          │  │
│   │              ⬆  Glissez votre fichier ici                │  │
│   │                  ou cliquez pour parcourir               │  │
│   │                                                          │  │
│   │       CSV (UTF-8, ; ou ,) — XLSX — max 5 Mo, 5 000 lignes│  │
│   │                                                          │  │
│   └──────────────────────────────────────────────────────────┘  │
│                                                                  │
│  Historique récent                              [Voir tout]      │
│  ───────────────────────────────────────────────────────────────│
│  portefeuille_mai_2026.csv     ✓ Importé      22/05  par F. Koné │
│  portefeuille_avr_2026.csv     ✗ Échec        15/04  par F. Koné │
│  …                                                                │
└──────────────────────────────────────────────────────────────────┘

Components: existing Button (primary "Télécharger le modèle"), existing Card for the dropzone wrapper. New component needed: FileDropzone (drag-and-drop + click-to-browse + progress bar + file-type validation). New component needed: Badge (status pills — already flagged HIGH in design-system.md § Needed Components).

5 states for the dropzone:

State Visual
Loading (after drop, while uploading) Dropzone collapses; progress bar 0→100%; label "Téléversement en cours…". Toast blocked until result.
Empty (initial) The drag-target above.
Error (HTTP 4xx — file too big, format) Dropzone returns; toast error with the specific French message from § 4.1; offending file shown in dropzone with a "Retirer" link.
Offline / low-network OP-console is online-first; on offline detection, disable the dropzone with overlay "Vous êtes hors ligne — l'import nécessite une connexion." Operator side, not customer side; no resume required.
Success Dropzone collapses; toast success "Fichier reçu. Validation en cours…"; auto-redirect to the import detail page within 2 s.

5.1.2 Import detail page (per-import view)

┌──────────────────────────────────────────────────────────────────┐
│  ← Imports                                                       │
│  portefeuille_mai_2026.csv                       [✓ Importé]     │
│                                                                  │
│  Téléversé le 22/05/2026 à 14:32 par Fatou Koné                  │
│  ───────────────────────────────────────────────────────────────│
│  ┌──────────┬──────────┬──────────┬──────────┐                   │
│  │ Lignes   │ Créés    │ Mis à j. │ Ignorés  │                   │
│  │   142    │    87    │    55    │    0     │                   │
│  └──────────┴──────────┴──────────┴──────────┘                   │
│                                                                  │
│  Aucune anomalie.                                                │
│                                                                  │
│  [Télécharger le fichier source]                                  │
└──────────────────────────────────────────────────────────────────┘

If PARSE_FAILED, swap the body with the anomaly table (§ 5.2 wireframe applies, read-only) + a prominent "Télécharger le rapport d'erreurs (.json)" button.

If DUPLICATE, show banner: « Ce fichier est identique à l'import du JJ/MM/AAAA. Aucune action requise. »

5.2 V1 — Atelier de correction (correction workshop)

Anchored decisions (locked here so V1 implementation has a reference):

  • Layout: full-width DataTable, virtualised at 200 rows/page. Sticky header. Sticky footer with summary + commit button.
  • Inline editing: only the 4 corrigeable fields per PRD § A.3 ING-US-02 — telephone, date_naissance, montant_du, code_agence. Other anomalies (IDENTIFIANT_CLIENT_ABSENT, DOUBLON_INTRA_FICHIER, etc.) are read-only with a row-level message: « Cette anomalie n'est pas corrigeable ici. Corrigez le fichier source et re-téléversez. »
  • Validation: each cell edit is a PATCH round-trip per PRD § C.4 (no optimistic UI — operator side is online).
  • PII masking: birth-date displayed as ••/••/AAAA in the table; full value revealed only inside the active edit input (PRD § C.3).
  • Commit gating: the "Valider l'import" button is disabled while any row has status BLOCKED. Tooltip on hover: « 17 ligne(s) à corriger avant de pouvoir valider. »
  • Cancel: secondary destructive Button variant="danger" "Annuler cet import". Confirmation Modal: « Annuler cet import supprimera toutes les corrections en cours. Cette action est irréversible. »
┌─────────────────────────────────────────────────────────────────────────────┐
│  ← Imports / portefeuille_mai_2026.csv          [En attente — 17 anomalies] │
│                                                                             │
│  17 ligne(s) à corriger      [filtres: Toutes ▾ | Anomalies seulement ☑]    │
│  ─────────────────────────────────────────────────────────────────────────  │
│  # │ Client     │ Téléphone        │ Né(e) le    │ Montant XOF │ Agence │ ⚠ │
│  ─ │ ────────── │ ──────────────── │ ─────────── │ ─────────── │ ────── │ ─ │
│  3 │ KONE F.    │[ 07 12 34      ⚠]│ ••/••/1984  │     250 000 │ ABJ-01 │ 1 │
│  8 │ DIALLO A.  │ +225 07 12…     │ ••/••/1990  │[     -500   ⚠] ABJ-03 │ 1 │
│  …                                                                         │
│  ─────────────────────────────────────────────────────────────────────────  │
│  17 à corriger · 0 surveillance · 142 prêtes      [Annuler]  [Valider ▸]    │
└─────────────────────────────────────────────────────────────────────────────┘

Components used / needed: - Existing: Button, Input, Modal, Toast, Select (filter), Checkbox. - New (already flagged in design-system.md): Badge (status + anomaly count), DataTable (sortable + filterable, virtualised), Skeleton (loading state for the table).

5 states for the workshop:

State Visual
Loading Skeleton rows in the table; sticky footer disabled.
Empty Banner: « Aucun import en attente de correction. » + CTA "Téléverser un fichier". (This is the post-commit / post-abandon empty state.)
Error (PATCH failed for a cell) Cell border red; inline helperText with the French anomaly message; toast error if the failure is global (e.g., session expired).
Offline / low-network Banner top-of-page: « Vous êtes hors ligne — vos corrections ne peuvent pas être sauvegardées. Reconnectez-vous pour continuer. » Cells become read-only with a dimmed style.
Success (after commit) Toast success « Import validé : {n} contrats créés, {m} mis à jour. » + redirect to the import detail page (§ 5.1.2 success layout).

5.3 V1 — Imports history list

Standard OP-console table page. Decisions:

  • Default sort: created_at desc.
  • Filters: status (multi-select), agency (admin only; operator scoped to assigned agencies), date range, uploader.
  • Filters survive reload (URL params).
  • Click row → import detail page.
  • Stale-import badge (per PRD § R-019): when an import is STAGED_WITH_ANOMALIES for ≥1 day, the row gets an amber border and a tooltip « Import en attente depuis {N} jour(s). »

6. Responsive behavior

ING is OP-console-only — desktop-first per PRD § C.4 + V1 § 30.3. No mobile mode is designed for V1; the OP-console shell already locks the operator surface to desktop layouts.

Breakpoint Behavior
Desktop ≥ 1024px Primary target. Full table layouts, sticky footer, dropzone at full width.
Tablet 768–1023 Supported but de-prioritised. Table columns collapse to "primary + expandable detail" pattern (V1, deferred).
Mobile < 768 Not supported. Show banner: « L'import nécessite un écran plus large. Connectez-vous depuis un ordinateur. »

7. Compliance UI

Two surfaces, both forward-state (v0.0.3+):

While feature.artci.phone-auth.granted=false on a production environment, the upload UI (v0.0.3) must render a blocking banner above the dropzone:

Ingestion en production temporairement désactivée. L'autorisation ARTCI pour le traitement des numéros de téléphone est en cours d'instruction. Vous pouvez préparer vos fichiers et utiliser l'environnement de pré-production, mais l'import en production est désactivé jusqu'à délivrance de l'autorisation. [En savoir plus] (→ link to legal page)

Backend already short-circuits the cron via ArtciProdGate (ING-005). The banner makes the silence legible to the operator.

7.2 Data-processing disclosure — CIMA Rég. 01-24 / Loi n°2013-450

A footer link "Traitement des données" on the imports page, opening a Modal with the standard French text:

Les données importées (nom, téléphone, date de naissance, contrat, montant) sont traitées par la Solution Papillon en qualité de sous-traitant du courtier responsable de traitement, dans le cadre du renouvellement des contrats d'assurance. Conservation des fichiers source : 12 mois. Conservation des traces d'import : 12 mois. Vos droits (accès, rectification, opposition) s'exercent auprès de votre courtier ou via courrier électronique à dpo@<courtier>.ci.

Cadre légal : Loi ivoirienne n°2013-450 sur la protection des DCP ; Règlement CIMA n°01-24.

[LEGAL-REVIEW] — text is preliminary; final wording requires the legal memo's INCERTAIN items (PRD § A.7) to be resolved by counsel before the v0.0.3 release.


8. Component specs — net-new components ING surfaces will require

Order of priority (matches design-system.md § Needed Components):

Component Used by Variants / states Accessibility
Badge (HIGH) Status pills (all ING list/detail screens) 6 semantic variants (info, success, warning, error, neutral, outline); 2 sizes (sm, md); optional leading dot Color + label (never color alone, per Design Principle § 6). aria-label mirrors the visible text.
DataTable (HIGH) Imports history, correction workshop Sortable headers, sticky header, virtualised body (200/page), row selection (checkbox), inline-edit cell variant, loading skeleton, empty state slot Keyboard nav (arrow keys between cells, Tab between editable cells, Enter to edit, Esc to cancel). ARIA role="grid".
FileDropzone (NEW) Upload UI (v0.0.3) States: idle, dragover, uploading (progress 0–100), error, disabled Click-to-browse fallback (<input type="file"> keyboard accessible). Drag events have aria-live polite announcements ("Fichier prêt à téléverser : {nom}").
Skeleton (MEDIUM) DataTable loading, dropzone loading Block, line, circle variants matching final component shape aria-busy="true" on the parent.

All new components live under frontend/src/components/ui/ and consume only design tokens — no hardcoded hex.


9. What's intentionally out of scope

  • No customer-side UI. ING is purely operator-facing (PRD § A.2 — Client assuré : Aucun).
  • No mobile design. OP-console is desktop-first.
  • No offline-resumable upload. Operator side is online-first; PRD § C.4 explicitly rules out optimistic UI.
  • No v0.0.0 wireframes. Backend cron only — Phase 3 of /design intentionally skipped.
  • No AI-suggested corrections (V3 per PRD § A.5).

10. Open questions surfaced for future iterations

These are not v0.0.0 blockers. Logged here so future /design sessions for v0.0.3+ have a head start:

  1. PRD § A.6 Q4 — "Supprimer cette ligne" button in V1 workshop for un-correctable rows: design recommendation is yes, with a confirmation Modal (avoid forcing a full re-upload for 1–2 broken rows). To validate with PO before V1.
  2. Stale-import badge dismissal (PRD § R-018): should the badge auto-dismiss on commit/abandon, or require an explicit "Fermer" action? Recommendation: auto-dismiss on terminal state; manual dismiss only for EXPIRED (which is the alert-worthy case).
  3. Template download (ING-US-05): should the XLSX include data-validation drop-downs for code_agence (populated from the tenant's agencies)? Recommendation: yes — closes the loop on the most common CODE_AGENCE_INCONNU mistake. V1.
  4. History row click: open detail in same tab (current OP-console pattern) vs. side drawer? Recommendation: same tab — anomaly tables are too wide for a drawer. Decide alongside the broader OP-console pattern in op-design.md.

Design spec produced by the DESIGNER personality (Claude Code) — session of 2026-05-22. v0.0.0 ships no UI; this document locks the French micro-copy library that backend emits today and pre-commits the forward UX so v0.0.3 / V1 slices land on a designed foundation rather than starting from scratch.