v1.20.7 — Draft Display Registry (ADR-085) + unified HITL pre-confirmation preview
Highlights
Single declarative source of truth for every DraftType's display. Replaces 4 disjoint legacy tables (DRAFT_TYPE_EMOJIS, DRAFT_SUCCESS_MESSAGES/DRAFT_CANCEL_MESSAGES, _DRAFT_RESULT_FIELD_CONFIG, hardcoded label-extraction chain) with one registry keyed by DraftType (apps/api/src/domains/agents/drafts/display.py), exhaustive over the 16 enum values. assert_registry_completeness() runs at lifespan startup and in a CI-parametrized test — a new DraftType without a registry entry either fails the boot or fails the PR.
Same registry also powers the pre-confirmation preview via a unified helper format_hitl_item_preview() consumed by both DraftCritiqueInteraction._generate_batch_critique (removed a ~100-line per-domain if/elif chain) and ForEachConfirmationInteraction._build_item_previews_section (with a new _steps_to_draft_type that maps FOR_EACH tool_name → canonical DraftType, falling back gracefully for non-draft domains like places/weather/routes).
What the user sees
Before (Supprime tous mes rappels × 3):
✅ 3/3
✅ Action exécutée avec succès
✅ Action exécutée avec succès
✅ Action exécutée avec succès
After:
🔔 ✅ 3 rappels supprimés
✅ **Faire les courses** — vendredi 16 mai 2026 à 14h00
✅ **Appeler Maman** — samedi 17 mai 2026 à 09h00
✅ **Rendez-vous médecin** — dimanche 18 mai 2026 à 11h00
And the pre-confirmation panel now renders consistently across both internal paths:
⚠️ **Confirmation de suppression**
**Éléments concernés :**
- 🔔 Rappel : Médecin - dimanche 17 mai 2026 à 19:00
- 🔔 Rappel : Ramonage - jeudi 21 mai 2026 à 19:00
- 🔔 Rappel : Alsace - vendredi 22 mai 2026 à 19:00
⚠️ Cette action est irréversible.
**Confirmes-tu cette suppression ?**
Every other draft type inherits the same shape automatically: 📅 Événement : Réunion équipe - lundi 20 mai 2026 à 10:00, 📧 Email : Confirmation rdv - jeudi 16 mai 2026 à 14:00, 👤 Contact : Marie Dupont, ✅ Tâche : Préparer démo - mardi 20 mai 2026 à 17:00.
Grammatical i18n across 6 languages
Two new tables encode noun forms (singular/plural + gender for fr/es/it) and past-participle forms (4 gender/number combinations for fr/es/it, invariant string for en/de/zh-CN); RESULT_HEADER_TEMPLATES handles Chinese's different word order (已删除 3 个提醒). Per-language pluralization rule: French treats 0 and 1 as singular; English/Spanish/German/Italian treat 1 as singular and everything else as plural; Chinese invariant. reminder_delete parity restored across DRAFT_SUCCESS_MESSAGES, DRAFT_CANCEL_MESSAGES, and _DESTRUCTIVE_CONFIRM_ACTION_TITLES in all 6 languages.
Tests
tests/unit/domains/agents/drafts/test_display_registry.py— ~30 parametrized cases onDraftType × 6 languagesfor the registry (completeness, i18n parity, plural-form rules, header composition snapshots).tests/unit/domains/agents/drafts/test_hitl_item_preview.py— 119 cases including the regression guard on the duplicated-emoji bug, per-language noun capitalization, per-DraftTypesmoke (16 × 6 languages = 96 cases), nested field resolution.tests/unit/domains/agents/drafts/test_for_each_draft_type_mapping.py— 25 cases ontool_name → draft_typeresolution.tests/unit/domains/agents/nodes/test_format_draft_execution_result.py— 19 cases covering batch/single/cancel/error per type + per-language header snapshots.
Architecture
ADR-085 — Draft Display Registry — full diagnostic story, decision rationale, alternatives considered, and consequences.
Backwards compat
HitlMessages.get_draft_emoji()unchanged (becomes a one-line wrapper over the registry — the three external callsites indraft_critique.pyare untouched).- The legacy generic FOR_EACH fallback (
item_date_connector) remains for non-draft domains (places/weather/routes/web_fetch/mcp). - All existing unit tests in
tests/unit/domains/agents/continue to pass without modification (4194 passed).
Files
- 31 files changed, 2624 insertions(+), 299 deletions(-).
- 7 new files: registry module + 4 test files + ADR +
__init__.py. - Backend: Black + Ruff + MyPy strict + 4194 unit tests green.
- Frontend: ESLint + tsc + 51 vitest cases green.
- i18n parity: en ↔ fr/es/de/it/zh OK on all keys.
🤖 Generated with Claude Code