19 KiB
phase, plan, type, wave, depends_on, autonomous, requirements, files_modified, estimate, must_haves
| phase | plan | type | wave | depends_on | autonomous | requirements | files_modified | estimate | must_haves | |||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick-260916-hiv | 01 | execute | 1 | true |
|
|
|
|
Purpose: Bei EWS scheiterte die Verbindung, wenn Anwender nur den Servernamen eintrugen. Ein sprechendes Beispiel und ein Hinweis verhindern das, ohne dass jemand die Anleitung lesen muss. Output: fuenf neue Uebersetzungsschluessel (de/en), die angepasste Komponente, ein neuer Komponententest, ein Changelog-Eintrag.
<execution_context>
@/.claude/gsd-core/workflows/execute-plan.md
@/.claude/gsd-core/templates/summary.md
</execution_context>
Gemessene Fakten zur Planungszeit (2026-09-16, Arbeitsbaum sauber auf main @ 2a820b6):
tin der Form istuseTranslations('widgets')(Zeile 63); allecalendar.formField*-Schluessel liegen im JSON unterwidgets.calendar(de.json/en.json Zeilen 205-236)."formFieldUrl"steht in beiden Dateien in Zeile 220, danach folgt"formFieldUsername".- Muster fuer Beispiel-URLs:
emailAlerts.hostPlaceholderExchange(de.json:983https://mail.firma.de/EWS/Exchange.asmx; en.json:983 weicht dort mitcompany.comab — fuer DIESEN Auftrag sind die Platzhalter laut Vorgabe in beiden Sprachen identisch). - Es gibt keinen Test fuer
calendar-source-form.tsx;widget-settings-panel.test.tsxliefert das Mock-Muster (echtede.jsonuebervi.mock('next-intl', …), Namespace-Verkettungns.key). Vitest: jsdom,globals: true, Setupsrc/test/setup.ts, Alias@→src. umlaut-guard.spec.tsprueft (a) keine Ersatzschreibung ausUMLAUT_REPLACEMENTS, (b) jedesae/oe/ue/ss-Wort in de.json muss aufUMLAUT_ALLOWLISTstehen, (c) identische Schluesselmengen de/en. Von den neuen Texten ist nurAdresseverdaechtig und bereits allowlisted —umlaut-dictionary.tsbleibt unangetastet.## UnveröffentlichtinCHANGELOG.md(Zeile 5) ist leer; direkt darunter folgt## 1.1.0 – 2026-09-16. Bestehende Eintraege beginnen mit einem Bereichsnamen wie „Kalender-Einstellungen: …“.- Basislinie:
pnpm --filter @tessera/web type-checkExit 0 (3 s);pnpm --filter @tessera/web exec vitest run→ 49 Testdateien / 309 Tests gruen; Umlaut-Waechter 3/3 gruen. biome checkist KEIN Gate: die Wurzel-biome.jsonscheitert unabhaengig von dieser Datei am unbekannten SchluesselorganizeImports(vorbestehend, nicht Teil dieses Auftrags —biome.jsonnicht anfassen).- Paketname ist
@tessera/web(nichtweb) — Filter immer--filter @tessera/web. - Kein Docker-Bau, kein Deploy, kein Testserver in diesem Auftrag (Deploy macht der User selbst).
formFieldUrlPlaceholderEws— Werthttps://mail.firma.de/EWS/Exchange.asmxformFieldUrlPlaceholderGraph— Werthttps://graph.microsoft.com/v1.0formFieldUrlPlaceholderCaldav— Werthttps://caldav.firma.de/dav/formFieldUrlPlaceholderIcs— Werthttps://…/kalender.ics(echtes Auslassungszeichen U+2026, wie beiformSavingim selben Block)formFieldUrlHintEws— de:Vollständige EWS-Adresse inkl. /EWS/Exchange.asmx eintragen – nur der Servername reicht nicht./ en:Enter the full EWS address including /EWS/Exchange.asmx – the server name alone is not enough.(Gedankenstrich U+2013 wie informFieldDomainHint).
Die vier Platzhalter sind in de.json und en.json IDENTISCH (Beispiel-Adressen, Vorgabe des Users). Nur der Hinweis ist uebersetzt. Beispiel-Domain ist ausschliesslich firma.de bzw. graph.microsoft.com — keine kundenspezifische Domain (Tessera ist ein Mehrfirmen-Produkt). Keine anderen Schluessel anfassen, umlaut-dictionary.ts nicht aendern (Adresse ist bereits allowlisted, sonst enthalten die Texte kein ae/oe/ue/ss-Wort). JSON muss gueltig bleiben (echte Umlaute direkt als UTF-8, wie im Bestand).
cd /home/vicolab/projects/tessera-ctl; set -e; for f in apps/web/src/messages/de.json apps/web/src/messages/en.json; do for k in formFieldUrlPlaceholderEws formFieldUrlPlaceholderGraph formFieldUrlPlaceholderCaldav formFieldUrlPlaceholderIcs formFieldUrlHintEws; do test "$(grep -c ""$k"" "$f")" -eq 1; done; done; node -e "const de=require('./apps/web/src/messages/de.json').widgets.calendar, en=require('./apps/web/src/messages/en.json').widgets.calendar; for (const k of ['formFieldUrlPlaceholderEws','formFieldUrlPlaceholderGraph','formFieldUrlPlaceholderCaldav','formFieldUrlPlaceholderIcs']) { if (de[k]!==en[k]) throw new Error('de/en differ: '+k); if (!/^https:///.test(de[k])) throw new Error('not https: '+k); } if (de.formFieldUrlPlaceholderEws!=='https://mail.firma.de/EWS/Exchange.asmx') throw new Error('EWS placeholder'); if (de.formFieldUrlPlaceholderGraph!=='https://graph.microsoft.com/v1.0') throw new Error('Graph placeholder'); if (de.formFieldUrlPlaceholderCaldav!=='https://caldav.firma.de/dav/') throw new Error('CalDAV placeholder'); if (!de.formFieldUrlPlaceholderIcs.endsWith('/kalender.ics')) throw new Error('ICS placeholder'); if (!de.formFieldUrlHintEws.includes('/EWS/Exchange.asmx') || !en.formFieldUrlHintEws.includes('/EWS/Exchange.asmx')) throw new Error('hint'); if (de.formFieldUrlHintEws===en.formFieldUrlHintEws) throw new Error('hint not translated'); const keys=Object.keys(de); const i=keys.indexOf('formFieldUrl'); if (keys[i+1]!=='formFieldUrlPlaceholderEws' || keys[i+5]!=='formFieldUrlHintEws') throw new Error('order'); console.log('I18N_OK')"; ! grep -q 'ctl.de' apps/web/src/messages/de.json apps/web/src/messages/en.json; pnpm --filter @tessera/web exec vitest run src/messages/umlaut-guard.spec.ts
Beide Sprachdateien enthalten die fuenf Schluessel genau einmal, direkt hinter formFieldUrl, mit den vorgegebenen Werten (Platzhalter identisch, Hinweis uebersetzt, keine kundenspezifische Domain); umlaut-guard.spec.ts bleibt 3/3 gruen (Schluesselparitaet de/en, keine Ersatzschreibung).
- Nach
const isExchange = type === 'exchange';(Zeile 81) zwei reine Ableitungen ohne State ergaenzen (keinsetStateim Render — siehe Kommentar Zeile 100-102):const isEws = isExchange && exchangeMode === 'ews';undconst urlPlaceholder, das per Verzweigung liefert: beiisExchange→isEws ? t('calendar.formFieldUrlPlaceholderEws') : t('calendar.formFieldUrlPlaceholderGraph'); beitype === 'caldav'→t('calendar.formFieldUrlPlaceholderCaldav'); beitype === 'ics'→t('calendar.formFieldUrlPlaceholderIcs'); sonst (kein Typ gewaehlt) der bisherige Festwerthttps://. KeinuseMemonoetig. - Im URL-Input (
id="source-url", Zeile 247-264) das festeplaceholder-Attribut (Zeile 251) aufplaceholder={urlPlaceholder}umstellen. Sonst nichts am Input aendern (Validierung, Klassen, Handler bleiben). - Direkt NACH dem bestehenden Fehlerabsatz
{urlError && (<p className="mt-1 text-xs text-destructive">…</p>)}(Zeile 265-267) einen zweiten bedingten Absatz einfuegen:{isEws && (<p data-testid="source-url-hint-ews" className="mt-1 text-xs text-muted-foreground">{t('calendar.formFieldUrlHintEws')}</p>)}. Entscheidung (von den zwei erlaubten Varianten): der Hinweis ist bei EWS IMMER sichtbar und steht bei einem Fehler UNTER dem Fehler — so hilft er auch dann, wenn die Eingabe gerade abgelehnt wird. - Den Doku-Kommentar der Komponente (Zeile 48-56) um einen Satz ergaenzen: Platzhalter des URL-Feldes typabhaengig, EWS-Hinweis unter dem Feld (Quick 260916-hiv). Keine Schluesselnamen im Kommentar aufzaehlen und den alten Attributwert nicht im Kommentar zitieren.
Keine weiteren Aenderungen: EXCHANGE_MODES, SOURCE_TYPES, handleTest, handleSubmit, Payload bleiben unveraendert. Keine neuen Pakete.
cd /home/vicolab/projects/tessera-ctl; set -e; F=apps/web/src/components/settings/calendar-source-form.tsx; test "$(grep -c 'placeholder="https://"' "$F")" -eq 0; test "$(grep -c 'placeholder={urlPlaceholder}' "$F")" -eq 1; for k in formFieldUrlPlaceholderEws formFieldUrlPlaceholderGraph formFieldUrlPlaceholderCaldav formFieldUrlPlaceholderIcs formFieldUrlHintEws; do test "$(grep -c "calendar.$k" "$F")" -ge 1; done; test "$(grep -c 'data-testid="source-url-hint-ews"' "$F")" -eq 1; test "$(grep -c 'text-muted-foreground' "$F")" -ge 1; test -f apps/web/src/components/settings/calendar-source-form.test.tsx; pnpm --filter @tessera/web exec vitest run src/components/settings/calendar-source-form.test.tsx; pnpm --filter @tessera/web type-check
Der neue Test (6 Faelle) ist gruen, Type-Check Exit 0; die Komponente liest den Platzhalter aus urlPlaceholder (kein fester Wert mehr im Attribut), rendert den grauen Hinweis nur bei Exchange + EWS und dort unterhalb eines eventuellen Fehlers.
- Kalender-Einstellungen: Das Feld „Adresse (URL)“ im Formular für Kalenderquellen zeigt jetzt je nach Typ ein passendes Beispiel (z. B. https://mail.firma.de/EWS/Exchange.asmx für Exchange EWS) und bei Exchange EWS einen Hinweis, dass die vollständige Adresse nötig ist – der Servername allein reicht nicht.
Stil wie die Bestandseintraege: Alltagssprache, echte Umlaute, typografische Anfuehrungszeichen „…“, keine Dateinamen, keine Commit-Kuerzel. Abschnitte ## 1.1.0 und ## 1.0.0 unveraendert lassen. Die Seite „Was ist neu“ zeigt diesen Abschnitt auf der Beta automatisch, sobald er einen Listenpunkt hat (filterChangelogForChannel blendet nur leere Abschnitte aus) — dort ist nichts zu tun.
cd /home/vicolab/projects/tessera-ctl; set -e; SEC="$(awk '/^## Unveröffentlicht$/{f=1;next} /^## /{if(f)exit} f' CHANGELOG.md)"; test "$(printf '%s\n' "$SEC" | grep -c '^### Geändert$')" -eq 1; test "$(printf '%s\n' "$SEC" | grep -c '^- Kalender-Einstellungen: Das Feld „Adresse (URL)“')" -eq 1; test "$(printf '%s\n' "$SEC" | grep -c 'Exchange.asmx')" -eq 1; test "$(printf '%s\n' "$SEC" | grep -c '^- ')" -eq 1; test "$(grep -c '^## Unveröffentlicht$' CHANGELOG.md)" -eq 1; test "$(grep -c '^## 1.1.0 – 2026-09-16$' CHANGELOG.md)" -eq 1; test "$(grep -c '^## 1.0.0 – 2026-09-15$' CHANGELOG.md)" -eq 1; pnpm --filter @tessera/web exec vitest run src/lib/changelog.test.ts
## Unveröffentlicht enthaelt genau eine Untergruppe ### Geändert mit genau einem Listenpunkt zum Kalenderquellen-Formular; die Versionsabschnitte 1.1.0 und 1.0.0 sind unveraendert; changelog.test.ts bleibt gruen.
<threat_model>
Trust Boundaries
| Boundary | Description |
|---|---|
| Browser-UI → i18n-Text | Platzhalter und Hinweis sind statische Uebersetzungsstrings; sie werden als React-Textknoten gerendert (automatisch escaped), nicht als HTML. Keine Nutzereingabe fliesst in Platzhalter oder Hinweis. |
STRIDE Threat Register
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|---|---|---|---|---|---|
| T-HIV-01 | Information Disclosure | Platzhalter-Werte in de.json/en.json | low | mitigate | Nur neutrale Beispiel-Domains (firma.de, graph.microsoft.com); Gate in Task 1 verbietet eine kundenspezifische Domain in beiden Sprachdateien. |
| T-HIV-02 | Tampering | Hinweis-Text im DOM | low | accept | Reiner Uebersetzungsstring ueber t(), als Textknoten gerendert — kein dangerouslySetInnerHTML, keine Interpolation von Nutzereingaben. |
| T-HIV-SC | Tampering | npm-Installationen | low | accept | Dieser Plan installiert keine Pakete (kein pnpm add); Lockfile bleibt unveraendert. |
| </threat_model> |
pnpm --filter @tessera/web type-check→ Exit 0.pnpm --filter @tessera/web exec vitest run→ 50 Testdateien gruen (49 Bestand +calendar-source-form.test.tsx), mindestens 315 Tests (309 + 6), keine Fehlschlaege.git diff --statberuehrt genau die fuenf Dateien ausfiles_modified(plus SUMMARY/Planungsdateien) — keinbiome.json, keinumlaut-dictionary.ts, keinpnpm-lock.yaml.- Kein Docker-Bau und kein Deploy in diesem Auftrag; die Browser-Pruefung auf der Beta macht der User nach dem naechsten Pull.
<success_criteria>
- Adressfeld zeigt je Typ das vorgegebene Beispiel als Platzhalter (EWS / Graph / CalDAV / ICS), ohne Typ weiterhin
https://. - Grauer EWS-Hinweis erscheint nur bei Exchange + EWS, unterhalb eines eventuellen Fehlers, Text aus
widgets.calendar.formFieldUrlHintEws(de/en). - de.json und en.json tragen dieselben fuenf Schluessel; Umlaut-Waechter gruen; keine kundenspezifische Domain.
- Neuer Komponententest mit 6 Faellen gruen; Type-Check gruen; Gesamt-Testlauf gruen.
- CHANGELOG.md:
## Unveröffentlicht→### Geändertmit genau einem Eintrag zum Kalenderquellen-Formular. </success_criteria>