28 KiB
Bundle: UI-Polish-Sprint Q2 (Format-Hints, UDB-Container, Store, Modal-Audit, Node-Mapping)
Beschreibung und Kontext
Sammelplan für fünf zusammenhängende UX-Issues, die als ein Sprint-Bundle umgesetzt werden, weil sie sich technisch ähneln (Metadaten-getriebenes Rendering) bzw. denselben Audit-Charakter haben.
Themen:
- Format-Hints für Pydantic-Felder (
frontend_format+frontend_format_labels) – Default-Rendering wie heute, Override per optionalem String/Liste injson_schema_extra. - UDB-Container drag&drop in Chat – nicht nur einzelne Records/Tabellen, sondern auch Gruppierungs-Knoten (
_GroupFolderView,_ParentGroupView,_MandateGroupView). - Store-Seite zeigt
automation-Feature zur Aktivierung an. - Modal-Forms schliessen nicht mehr durch Outside-Klick – Audit über alle Ad-hoc-Modals;
usePromptist primärer Verursacher. - Graph-Editor-Nodes: Field-Mapping verständlich machen – Ports tragen optional dieselben
frontend_type/frontend_format-Hints; DataPicker zeigt formatierte Preview-Werte. - Outlook: Mail-Management-Tools für Agent + Mail-Limit-Audit – Adapter erhält
replyToMail/replyAllToMail/forwardMail(+ Draft-Varianten),moveMail/copyMail,deleteMail/archiveMail,markMailAsRead/markMailAsUnread,flagMailauf Basis von Microsoft Graph; Agent-Toolboxemailexposed sie. Zusätzlich Investigation, warum trotz_DEFAULT_MESSAGE_LIMIT=100der User effektiv nur ~20 Mails sieht. - UDB: Einrückung aktiver Datenobjekte entfernen – In der UDB werden persönliche Datenquellen-Rows nach Aktivierung (Icon-Klick) optisch leicht eingerückt. Diese Indentation ist visuell verwirrend (suggeriert eine Hierarchieebene, die nicht existiert) und soll weg. Padding zwischen aktiviertem und inaktivem Zustand muss identisch sein.
- i18n-Registrierung für Feature-DATA_OBJECTS / RESOURCE_OBJECTS (systemisch) – Bare-String-Labels in
mainRedmine.py,mainTrustee.pyetc. werden in nicht-DE-Sprachen als[Konfiguration],[Redmine-Tickets (Mirror)]angezeigt, weilt()sie nie zur Build-Time registriert hat. Audit + Fix über alle Features.
Business-Treiber: Konsolidierung des UI-Polishs vor PWG-Pilot (siehe c-work/1-plan/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md); inkonsistente Zahlen-/Bool-Darstellungen blockieren Demo-Tauglichkeit.
Risiko bei Nicht-Umsetzung: Demos zeigen Roh-Floats wie 4567777788, Modals werden in Live-Demos versehentlich geschlossen, Anwender verstehen Node-Datenflüsse nicht.
Fokus und kritische Details
- Default-Verhalten unverändert: Ohne
frontend_formatrendert das UI exakt wie heute. Reine additive Änderung. Keine Migration, kein Feature-Flag. - i18n-Pfad:
frontend_formatist sprach-neutral (Format-Spec, Sprach-tokens wieCHF/kg/MBzulässig aber nicht übersetzt).frontend_format_labels(Liste) wird vom@i18nModel-Decorator genauso wielabelregistriert viat()und vom Backend bereits übersetzt ausgeliefert. usePrompt-Fix darf laufende Workflows nicht brechen – Backdrop-Klick muss überall durch ein bewusstes Cancel ersetzt werden, nicht durch komplettes Submit.- UDB-Container-Resolver muss
objectKey-Globs (Wildcard*) auf der Backend-Workspace-Context-Seite verstehen, sonst kommt nichts beim AI-Agent an. - Graph-Editor Ports:
PortFieldist heute backend-definiert innodeDefinitions/*.py. Keine Schemamigration, nur Schema-Anreicherung.
Fragile Stellen:
gateway/modules/shared/attributeUtils.py::getAttributesForTable– die zwei parallelen Pfade (field_info.extravs.json_schema_extra) sind heute schon redundant. Neue Felder durchgängig über beide Pfade lesen, sonst latenter Bug.frontend_nyla/src/components/FormGenerator/FormGeneratorTable/FormGeneratorTable.tsx::formatCellValue– wird auch im PDF-Export benutzt (prüfen). Format-Funktion muss reine Funktion sein.
Ziel und Nicht-Ziele
- Ziel: Fünf Issues gemeinsam, generische Mechanismen wo möglich (Format-Hints, Container-Glob), spezifische Punkt-Fixes wo nötig (Store-Eintrag,
usePrompt). - Ziel: 100% rückwärtskompatibel — kein Field-/Node-Definition muss angefasst werden, wenn nicht gewünscht.
- Explizit NICHT: Komplettes RenderHint-Objekt-Schema (initial vorgeschlagen, vom User verworfen — zu komplex).
- Explizit NICHT: Neue
FrontendType-Enums wieCURRENCY,PERCENT,BYTES. Stattdessen nurfrontend_format-String über bestehendemfrontend_type: "number"/"float"/"integer"/"boolean". - Explizit NICHT: Excel-formula-paritätischer Parser. Nur die im Spec-Block unten definierten Format-Tokens.
Format-Spec (Thema 1)
frontend_format-Syntax
[<ALIGN>:]<FORMAT>
ALIGN (optional, default = type-spezifisch: R für Zahlen, L für Text, M für Bool):
L= linksM= mittigR= rechts
FORMAT-Tokens:
| Token | Beispiel-Output | Bedeutung |
|---|---|---|
#'###.00 |
1'234'567.89 |
Schweizer Tausendertrenner mit Apostroph, Nachkommastellen via .00 |
0.00 |
1234.57 |
Feste Nachkommastellen, kein Tausendertrenner |
0 |
1235 |
Integer-Rundung |
0.0% |
12.3% |
Prozent (Multiplikation × 100) |
0.0e+0 |
1.2e+6 |
Scientific |
b |
12.3 MB |
Auto-Bytes (nutzt formatBinaryDataSizeBytes) |
<format> <UNIT> |
1'234.00 CHF |
Suffix-Unit, sprach-neutral |
<UNIT> <format> |
CHF 1'234.00 |
Prefix-Unit |
Beispiele:
R:#'###.00→1'444'555.67M:0→12L:0.000→4.556R:CHF #'###.00→CHF 1'234.50R:#'###.00 CHF→1'234.50 CHFR:b→4.5 GBR:0.0 kg→12.3 kgR:0.00%→45.67%
frontend_format_labels-Syntax
Liste von 2 oder 3 Strings, je nach Typ:
- Boolean (3 Werte):
["Ja", "—", "Nein"]fürtrue / null / false - Boolean ohne null (2 Werte):
["Ja", "Nein"](null fällt auf"—"zurück) - Unbenutzt für Zahlen.
@i18nModel registriert jeden Listeneintrag automatisch via t(label, "table.<Class>.<field>.label[N]", desc).
Beispiel-Anwendung
@i18nModel("Mietzinsbestaetigung")
class Mietzinsbestaetigung(PowerOnModel):
betrag: float = Field(
json_schema_extra={
"label": "Bruttomiete",
"frontend_type": "number",
"frontend_format": "R:#'###.00 CHF",
},
)
abrechnungsmonat: int = Field(
json_schema_extra={
"label": "Monat",
"frontend_type": "integer",
"frontend_format": "M:0",
},
)
ist_bezahlt: bool = Field(
json_schema_extra={
"label": "Bezahlt",
"frontend_type": "boolean",
"frontend_format_labels": ["Bezahlt", "—", "Offen"],
},
)
Ohne diese Hints: heutiges Default-Rendering bleibt.
Betroffene Module
- Gateway:
modules/shared/attributeUtils.py(Thema 1: durchschleifenfrontend_format,frontend_format_labels)modules/shared/i18nRegistry.py(Thema 1: Decorator scanntfrontend_format_labels)modules/routes/routeStore.py+ Feature-Registry (Thema 3:automationalscategory="store")modules/features/<workspaceContext-resolver>(Thema 2: Glob-Wildcards inobjectKey)modules/features/graphicalEditor/nodeDefinitions/*.py(Thema 5: optionalefrontendType+frontendFormatanPortField)modules/connectors/providerMsft/connectorMsft.py(Thema 6: neueOutlookAdapter-MethodenreplyToMail,replyAllToMail,forwardMail,createReplyDraft,createReplyAllDraft)modules/serviceCenter/services/serviceAgent/toolboxRegistry.py(Thema 6: Toolboxemailexposed neue Tools)modules/serviceCenter/services/serviceAgent/coreTools/_dataSourceTools.pyoder neues_emailTools.py(Thema 6: Tool-ImplementierungreplyToMailetc.)frontend_nyla/src/components/UnifiedDataBar/SourcesTab.tsx(Thema 7: Border-Left auf Z.1414 erzeugt 3px-Versatz; aktive Wildcard-Row braucht kompensierenden negativenmarginLeft)gateway/modules/features/*/main*.py(Thema 8: alleDATA_OBJECTS/RESOURCE_OBJECTS-Labels viat()registrieren – betrifft mind.mainRedmine.py,mainTrustee.py,mainCommcoach.py,mainChatbot.py)
- Frontend:
src/components/FormGenerator/FormGeneratorTable/FormGeneratorTable.tsx(Thema 1:formatCellValueruft neuenapplyFrontendFormat)src/components/FormGenerator/FormGeneratorForm/FormGeneratorForm.tsx(Thema 1: gleicher Hook für Read-Only-Anzeige)src/utils/applyFrontendFormat.ts(Thema 1: NEU – zentrale Format-Funktion, nutztformatAmount/formatBinaryDataSizeBytes)src/components/UnifiedDataBar/SourcesTab.tsx(Thema 2: Container-Buttons, sendToChat für Group-Views)src/pages/Store.tsx(Thema 3: nichts zu ändern – nur Backend-Config)src/hooks/usePrompt.tsx(Thema 4: Backdrop-onClickentfernen)src/components/UiComponents/Modal/Modal.tsx(Thema 4: Audit – ist bereits korrekt)src/components/FlowEditor/editor/NodeConfigPanel.tsx(Thema 5: DataPicker zeigt formatierte Werte)src/components/FlowEditor/nodes/*(Thema 5: Edges/Ports labeln)
- DB-Migration: nein (alles in-memory Schema)
- Andere: docs in
b-reference/aktualisieren
Entscheidungen
| Datum | Entscheidung | Begründung |
|---|---|---|
| 2026-04-21 | frontend_format als optionaler String, kein RenderHint-Objekt |
User-Feedback: Komplexität minimieren, an bestehendem frontend_*-Pattern andocken |
| 2026-04-21 | frontend_format_labels als separate Liste (statt Inline-Tokens im Format-String) |
i18n-Sammelmechanismus von @i18nModel greift nur auf diskreten Feldern; Inline-Parsing wäre fehleranfällig |
| 2026-04-21 | Keine neuen FrontendType-Enums |
Bestehende Typen (number, integer, boolean) reichen – Format ist orthogonal |
| 2026-04-21 | Audit aller Modal-Implementierungen, nicht nur usePrompt |
User explizit gefordert |
Umsetzungs-Checkliste
Thema 1 — Format-Hints
attributeUtils.py::getAttributesForTable:frontend_formatundfrontend_format_labelsausjson_schema_extraUNDfield_info.extralesen, inattr_defausgeben alsformatundformatLabelsi18nRegistry.py::i18nModel:frontend_format_labelsscannen, jedes Element viat()registrieren mit Keytable.<Class>.<field>.label[<idx>]getModelLabels()ergänzen, sodass übersetzte Format-Labels auf Frontend ausgeliefert werden- NEU
frontend_nyla/src/utils/applyFrontendFormat.tsmitapplyFrontendFormat(value, format, formatLabels, type, locale)– pure function FormGeneratorTable::formatCellValueintegrieren (fürcellAlignebenfalls nutzen)FormGeneratorFormintegrieren für read-only Anzeige- Unit-Tests für Format-Parser
- Beispiel-Anwendung in 2-3 bestehenden Models (
Mietzinsbestaetigung, eine Trustee-DTO)
Thema 2 — UDB-Container
SourcesTab.tsx: Buttons (chat / scope-cycle / neutralize-toggle) auf_GroupFolderView,_ParentGroupView,_MandateGroupViewergänzen- Container-
objectKey-Format definieren:data.feature.<code>.group:<groupKey>.*(Glob mit*) - Backend-Resolver
workspaceContext(ingateway/modules/aiAgent/): Globs auflösen, Records-Set zurückliefern - Anzeige im Chat-Context: "Container Mandant Müller AG (12 Records)" statt
null - Audit Token-Limits — Container kann gross sein, Warning-UX
Thema 3 — Store: automation
- In Feature-Registry (
gateway/modules/datamodels/datamodelFeatures.pyo.ä. Bootstrap):automationmitmeta.category = "store",enabled = True - Optional: Icon/Beschreibung für Store-Card prüfen
- Smoke-Test: Aktivierung in fresh-Mandant funktioniert
Thema 4 — Modal-Forms (Outside-Click-Fix)
usePrompt.tsx: Backdrop-onClick={_handleCancel}entfernen (oder optional via PropdismissOnBackdrop?: boolean = false)- Audit: grep nach
onClick.*Close|onClick.*Cancelauf Overlay-DIVs infrontend_nyla/src/ - Liste der gefundenen Stellen prüfen, jede einzeln korrigieren oder bewusst lassen (z.B. Image-Lightbox: Backdrop schliesst soll bleiben)
Modal.tsx: Default-Behavior in Doku festschreiben (closeOnOverlayClick=false)
Thema 5 — Graph-Editor Field-Mapping
PortField-Schema (Pydantic) erweitern um optionalfrontendType,frontendFormat,frontendFormatLabelsNodeConfigPanel.tsx: DataPicker-Preview nutztapplyFrontendFormat- Node-Card: zeige Input-Schema-Tag und Output-Schema-Tag inline (bisher nur in Detail-Panel)
- Edges: optional Label "Buha.account → posting.kontoNr" anzeigen wenn Port-Mapping nicht 1:1
- Neuer
frontendType: "wireOnly"für Parameter, die ausschliesslich via Wire befüllt werden (verhindert UI-Eingabe) - 3 Beispiel-Nodes (Trustee, Redmine, Mietzins) mit kompletten Hints versehen
Thema 6 — Outlook Mail-Management + Mail-Limit-Audit
Reply / Forward-Tools
OutlookAdapter.replyToMail(messageId, body, bodyType="HTML", comment=None)→POST /me/messages/{id}/replymit Body{"comment": "..."}(für inline) oder{"message": {...}}(für vollen Override)OutlookAdapter.replyAllToMail(messageId, body, bodyType, comment)→POST /me/messages/{id}/replyAllOutlookAdapter.forwardMail(messageId, to, body, bodyType, comment)→POST /me/messages/{id}/forwardOutlookAdapter.createReplyDraft(messageId, body, bodyType)→POST /me/messages/{id}/createReply(gibt Draft-ID zurück)OutlookAdapter.createReplyAllDraft(messageId)→POST /me/messages/{id}/createReplyAllOutlookAdapter.createForwardDraft(messageId, to)→POST /me/messages/{id}/createForward- Agent-Tools
replyToMail,replyAllToMail,forwardMailregistrieren intoolboxRegistry.py(Toolboxemail) - Tool-Parameter:
dataSourceIdODERconnectionId+service,messageId(ausbrowseDataSource/searchDataSource-Pfad zu extrahieren),body,bodyType(Text/HTML),draft(bool, defaultfalse), optionaladditionalRecipients/cc - Tool-Beschreibung explizit: "Use this when responding to an existing email — preserves Subject (with
AW:/RE:-Präfix), Conversation-Thread und Quoted-Original-Message automatisch" - Anti-Pattern in
sendMail-Description einbauen: "DO NOT usesendMailto reply to an existing email — usereplyToMailinstead"
Move / Copy / Delete / Archive
OutlookAdapter.moveMail(messageId, destinationFolderId)→POST /me/messages/{id}/movemit{"destinationId": "<folderId>"}. Gibt neue Message-ID im Ziel-Ordner zurück (Graph erstellt eine neue ID).OutlookAdapter.copyMail(messageId, destinationFolderId)→POST /me/messages/{id}/copyOutlookAdapter.deleteMail(messageId, hardDelete=False):hardDelete=False(default) → Move in Well-Known-Folderdeleteditems(entspricht Outlook-Papierkorb-Verhalten)hardDelete=True→DELETE /me/messages/{id}(endgültig, ohne Papierkorb)
OutlookAdapter.archiveMail(messageId)→ Move in Well-Known-Folderarchive- Folder-Resolution-Helper:
_resolveFolderId(folderRef: str)akzeptiert:- Well-Known-Names (
inbox,archive,deleteditems,drafts,sentitems,junkemail) - Folder-IDs direkt (Graph-Format, beginnen typisch mit
AAMk...) - Display-Names (case-insensitive Match, mit Locale-Fallback
Posteingang/Archiv/Gelöschte Elementeetc.) — über bestehendebrowse("/")-Logik - Ggf. nested via
/Parent/Child
- Well-Known-Names (
- Agent-Tools
moveMail,deleteMail,archiveMail(kein separatescopyMailinitial – auf Anfrage) - Tool-Parameter:
dataSourceId|connectionId+service,messageId,destinationFolder(well-known-name oder display-name oder ID),hardDelete(für deleteMail) - Confirmation-Pattern für destruktive Operationen: Tool-Description macht klar, dass
deleteMailmithardDelete=trueirreversibel ist; Agent soll explizite User-Bestätigung einholen
Read-State / Flag-Tools (Bonus, da trivial)
OutlookAdapter.markMailAsRead(messageId, isRead=True)→PATCH /me/messages/{id}mit{"isRead": true|false}OutlookAdapter.flagMail(messageId, flagStatus)→PATCH /me/messages/{id}mit{"flag": {"flagStatus": "flagged|complete|notFlagged"}}- Tools
markMailAsRead,markMailAsUnread,flagMailregistrieren
Folder-Listing als Agent-Tool
listMailFoldersTool, das die bestehendebrowse("/")-Logik wrappt und dem Agent die verfügbaren Ordner-Namen + IDs zur Verfügung stellt – Voraussetzung damitmoveMailmit Display-Name oder Folder-ID arbeiten kann ohne Raten
Mail-Limit-Audit
- Investigation: Warum sieht User effektiv nur ~20 Mails, obwohl
_DEFAULT_MESSAGE_LIMIT=100?- Hypothese A: LLM ruft
browseDataSourcemitlimit=20(durch Tool-Beschreibung suggeriert?) - Hypothese B: Output-Truncation im Agent-Loop (
ToolResult.datawird auf N chars/zeilen gekürzt) - Hypothese C: System-Prompt enthält Limit-Hinweis
- Hypothese D: Frontend-UI für UDB-Browse zeigt nur erste N Records
- Hypothese A: LLM ruft
- In Tool-Description klarstellen: "Default returns up to 100 entries. Use
limitparameter only if user explicitly wants fewer/more." - Falls Output-Truncation aktiv: Limit auf min. ~32k chars setzen für Mail-Listings (Subjects sind kurz, 100 Mails ≈ 5–10kB)
- Doku in
b-reference/: Mail-Browse-Limits dokumentieren
Thema 7 — UDB Indentation aktive Rows
- Reproduktion: Zustand vor/nach Aktivierung eines persönlichen Daten-Objekts (Icon-Klick) per Screenshot festhalten – Pixelversatz dokumentieren
- In
SourcesTab.tsxRow-Renderer prüfen: allepaddingLeft-Berechnungen (depth * 16 + 4,depth * 16 + 8, statisch10/36, Sub-Block(depth + 1) * 16 + 20) - Aktiver/Selected-Zustand darf kein zusätzliches
paddingLeft/marginLeftbewirken - Falls visueller Indikator gewünscht: per
borderLeftmit kompensiertem negativemmarginLeftlösen, nie via Padding-Verschiebung - Hover-/Drag-Over-/Selected-Zustände bleiben pixelgenau gleich
- Analoges Problem in Feature-Sources-Views (
_GroupFolderView,_ParentGroupView,_MandateGroupView) prüfen und konsistent fixen
Thema 8 — i18n-Registrierung Feature-Catalog-Labels (systemisch)
- Root-Cause-Doku in Plan:
t(key)registriert Keys nur lazily zur Aufruf-Zeit. Wenn ein Label nie viat()(zur Modul-Import-Zeit) angefasst wurde, fehlt es in_REGISTRYzum Zeitpunkt der Übersetzungs-Sammlung → kein Translation-Eintrag → Fallback[key]für nicht-DE Sprachen - Audit-Script: Grep über
gateway/modules/features/*/main*.pynach Pattern"label":\s*"[^"]+"(bare string stattt(...)) - Fix in
mainRedmine.py: alle 5DATA_OBJECTS-Labels ("Konfiguration","Redmine-Verbindung","Redmine-Tickets (Mirror)","Redmine-Beziehungen (Mirror)","Alle Redmine-Daten") und alle 11RESOURCE_OBJECTS-Labels viat("…", context="UI")(oder eigener Context wie"feature.redmine") registrieren - Fix in
mainTrustee.py: identisch für DATA_OBJECTS (mind. 7 Labels:"Lokale Daten","Konfiguration","Daten aus Buchhaltungssystem","Position","Dokument","Buchhaltungs-Verbindung","Sync-Protokoll") und Page-/Resource-Labels - Fix in allen weiteren Features (
commcoach,chatbot,realEstate,automation,workspace,graphicalEditor,neutralization) – pro Feature 1 commit - Optional: Decorator/Helper
_dataObject(objectKey, label, **meta)einführen, dert()automatisch ruft. Damit wird der Bug zukünftig unmöglich. Pro Feature umstellen. - Translation-Cache neu generieren: Build/Sync-Skript für i18n-Files ausführen, neue Keys übersetzen lassen
- Frontend-Verifikation: UDB in EN/FR umschalten, sicherstellen dass Redmine/Trustee-Container saubere Labels zeigen (keine
[...])
Querschnitt
- RBAC / Permissions: keine Änderung
- Neutralisierung betroffen: nein (Format-Layer arbeitet auf Anzeige-Werten)
- Navigation / Routing: keine Änderung
- Billing-Impact: nein
- DB-Migration: nein
Akzeptanzkriterien
| # | Kriterium (Given-When-Then) | Prio |
|---|---|---|
| 1 | Given ein Float-Feld mit frontend_format="R:#'###.00 CHF", When im FormGeneratorTable angezeigt, Then 1234.567 rendert als 1'234.57 CHF, rechtsbündig |
must |
| 2 | Given ein Float-Feld OHNE frontend_format, When angezeigt, Then identisches Rendering wie vor dem Patch (Regression-Sicherheit) |
must |
| 3 | Given ein Bool-Feld mit frontend_format_labels=["Ja","—","Nein"], When Wert true/null/false, Then Anzeige zeigt Ja / — / Nein (mit t()-Übersetzung wenn Sprache en → Yes / — / No) |
must |
| 4 | Given UDB zeigt Mandanten-Gruppe mit 5 Records, When User klickt Chat-Icon auf Gruppe, Then Chat-Kontext erhält alle 5 Records | must |
| 5 | Given UDB-Container mit 1000+ Records, When User sendet an Chat, Then Warning-Toast bzgl. Token-Limit | should |
| 6 | Given Mandant mit automation-Feature nicht aktiviert, When /store öffnet, Then Card "Automation" sichtbar mit Aktivieren-Button |
must |
| 7 | Given Feature-Instance-Rename-Dialog offen, When User klickt ausserhalb des Dialogs, Then Dialog bleibt offen | must |
| 8 | Audit aller Modals erstellt mit Liste pro Datei + Soll-Verhalten | must |
| 9 | Given Graph-Editor-Node mit Output-Port betrag (frontendType=number, frontendFormat="R:CHF #'###.00"), When DataPicker im nächsten Node öffnet, Then Sample-Wert formatiert angezeigt |
should |
| 10 | Given Node-Card im Editor, When Mouse-Hover auf Input-/Output-Anchor, Then Tooltip zeigt Schema-Name + Beschreibung | should |
| 11 | Given Mail im Posteingang, When Agent das Tool replyToMail mit messageId und body aufruft, Then Outlook-Thread enthält neue Antwort als AW: <Original-Subject> mit korrektem In-Reply-To/Conversation-Id |
must |
| 12 | Given Tool replyToMail mit draft=true, When ausgeführt, Then im Drafts-Folder erscheint Antwort-Entwurf, nicht als neue Mail |
must |
| 13 | Given Posteingang mit 100 Mails, When browseDataSource ohne expliziten limit aufgerufen, Then Agent erhält tatsächlich ≥100 Subjects (verifiziert via Log/Output-Länge) |
must |
| 14 | Given Tool-Beschreibung von sendMail, When Agent eine bestehende Mail beantworten will, Then Beschreibung steuert ihn explizit auf replyToMail (manueller Prompt-Test) |
should |
| 15 | Given persönliche Daten-Quelle in UDB inaktiv, When Icon-Klick aktiviert sie, Then horizontale Position der Row-Inhalte (Icon, Label) bleibt pixelgenau identisch — keine Einrückung | must |
| 16 | Given dieselbe Row im Hover-/Selected-/Drag-Over-Zustand, When Zustand wechselt, Then keine horizontale Verschiebung sichtbar | should |
| 17 | Given Mail im Posteingang, When Agent moveMail mit destinationFolder="archive" aufruft, Then Mail liegt im Archiv-Ordner und ist nicht mehr im Posteingang |
must |
| 18 | Given Mail im Posteingang, When Agent deleteMail ohne hardDelete aufruft, Then Mail liegt in deleteditems (Papierkorb), Recovery möglich |
must |
| 19 | Given Mail, When Agent deleteMail mit hardDelete=true aufruft, Then Mail komplett entfernt; vorheriges User-Confirmation-Pattern dokumentiert in Tool-Description |
must |
| 20 | Given Agent kennt Ordner-Namen nicht, When listMailFolders aufgerufen, Then Liste mit Display-Namen + IDs (inkl. Well-Known-Aliases inbox/archive/deleteditems) zurück |
should |
| 21 | Given Agent ruft moveMail mit Display-Name "Archiv" (DE) auf, When Locale en ist, Then trotzdem korrekter Folder-Resolve über Well-Known-Alias archive |
should |
| 22 | Given Mail ungelesen, When Agent markMailAsRead aufruft, Then isRead=true in Outlook |
should |
| 23 | Given UDB in Sprache en, When User Redmine-Feature expandet, Then Container-Labels sind übersetzt (kein [Konfiguration] o.ä. sichtbar) |
must |
| 24 | Given Audit-Script über alle main*.py, When ausgeführt, Then Output zeigt 0 verbleibende Bare-String-Labels in DATA_OBJECTS/RESOURCE_OBJECTS |
must |
| 25 | Given neuer Feature-Entwickler ergänzt ein DATA_OBJECT, When er _dataObject(...)-Helper benutzt, Then i18n-Registrierung passiert automatisch ohne Mehraufwand |
should |
Testplan
| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
|---|---|---|---|---|---|
| T1 | 1,2,3 | unit | ja | frontend_nyla/src/utils/applyFrontendFormat.test.ts |
pending |
| T2 | 1,2,3 | api | ja | gateway/tests/test_attributeUtils_format.py |
pending |
| T3 | 3 | api | ja | gateway/tests/test_i18nModel_formatLabels.py |
pending |
| T4 | 4,5 | e2e | nein (manuell) | UDB → Chat Flow | pending |
| T5 | 6 | api | ja | gateway/tests/test_routeStore_automation.py |
pending |
| T6 | 7 | unit | ja | frontend_nyla/src/hooks/usePrompt.test.tsx |
pending |
| T7 | 8 | doc | nein | wiki/b-reference/modal-audit.md |
pending |
| T8 | 9,10 | manuell | nein | Graph-Editor Smoke | pending |
| T9 | 11,12 | api | ja | gateway/tests/test_outlookAdapter_reply.py (Mock Graph) |
pending |
| T10 | 13 | api | ja | gateway/tests/test_browseDataSource_mailLimit.py |
pending |
| T11 | 11,12,14 | manuell | nein | Real-Outlook Agent-Chat E2E | pending |
| T12 | 15,16 | manuell | nein | UDB Visual-Diff Screenshot vor/nach | pending |
| T13 | 17,18,19 | api | ja | gateway/tests/test_outlookAdapter_moveDelete.py (Mock Graph) |
pending |
| T14 | 20,21 | api | ja | gateway/tests/test_outlookAdapter_folderResolve.py |
pending |
| T15 | 22 | api | ja | gateway/tests/test_outlookAdapter_readState.py |
pending |
| T16 | 17,18,19,20 | manuell | nein | Real-Outlook E2E Move/Archive/Delete | pending |
| T17 | 23 | manuell | nein | Sprachwechsel UDB-Test (DE → EN/FR) | pending |
| T18 | 24 | script | ja | gateway/tests/test_featureCatalogLabels_i18n.py (Grep über alle main*.py) |
pending |
Phasen-Planung (Vorschlag)
| Phase | Inhalt | Geschätzt | Abhängigkeit |
|---|---|---|---|
| P1 | Thema 1 Backend (attributeUtils, i18nRegistry) + Tests T2/T3 |
0.5d | – |
| P2 | Thema 1 Frontend (applyFrontendFormat + Integration FormGenerator) + T1 |
0.5d | P1 |
| P3 | Thema 4 (usePrompt-Fix + Modal-Audit) + T6/T7 |
0.5d | – (parallel) |
| P4 | Thema 3 (Store Automation) + T5 | 0.25d | – (parallel) |
| P5 | Thema 2 (UDB-Container Backend-Resolver + Frontend-Buttons) | 1.0d | – (parallel) |
| P6 | Thema 5 (Graph-Editor Port-Hints + DataPicker-Format) | 0.75d | P2 (nutzt applyFrontendFormat) |
| P7 | Anwendung Format-Hints auf 3+ produktiven Models, Demo-Test | 0.5d | P2 |
| P8a | Thema 6 Reply/Forward-Adapter + Tools + T9 | 0.5d | – (parallel) |
| P8b | Thema 6 Move/Delete/Archive-Adapter + Folder-Resolver + Tools + T13/T14 | 0.75d | P8a |
| P8c | Thema 6 Read-State/Flag/listMailFolders + T15 | 0.25d | P8a |
| P9 | Thema 6 Mail-Limit-Investigation + Fix + T10 | 0.5d | P8a |
| P10 | Thema 7 UDB-Indentation-Fix + T12 | 0.25d | – (parallel) |
| P11a | Thema 8 Bare-String-Audit-Script + T18 | 0.25d | – (parallel) |
| P11b | Thema 8 Fix mainRedmine.py + mainTrustee.py + Translation-Sync |
0.25d | P11a |
| P11c | Thema 8 Fix übrige Features (commcoach, chatbot, realEstate, automation, …) | 0.5d | P11a |
| P11d | Thema 8 Optional _dataObject(...)-Helper-Refactoring |
0.25d | P11b/c |
Total: ~7.75 Personentage, parallelisierbar auf ~3 Kalendertage.
Links
- PR: tbd
- Issue:
local/notes/issues.txt(interne TODO-Liste) - Verwandt:
wiki/c-work/4-done/2026-04-generic-graph-editor.md(Typed Node Handover System – Basis für Thema 5)
Abschluss
- Code-Implementierung aller 14 Workitems (P1–P11c)
- Manueller End-to-End-Test durch User (UDB → Chat, Outlook-Agent, Modal-Forms, Format-Hints, Trustee-Tabellen-Header)
- Plan in
4-done/verschoben - Follow-up:
b-reference/data-model-fields.mdmit Format-Spec dokumentieren (separates Doku-Ticket) - Follow-up:
b-reference/modal-audit.mdals Living Document anlegen - Follow-up:
TOPICS.md-Eintrag „UI-Format-Hints (frontend_format)"
Erweiterungen während der Umsetzung
Während der Implementierung kamen vom User folgende Punkte hinzu, die direkt mit-erledigt wurden:
- Trustee Daten-Tabellen-Header: Tab-Bar nach UDB-Struktur in 4 Kategorien (Stammdaten / Lokale Daten / Konfiguration / Daten aus Buchhaltungssystem) gruppiert;
(read-only)-Text durch Lock-Icon (🔒) ersetzt. Tab-Labels 1:1 mit UDB-Labels synchronisiert (z.B. „Kontenplan" statt „Konten (Sync)"). Datei:frontend_nyla/src/pages/views/trustee/TrusteeDataTablesView.tsx.